# HG changeset patch # User Sebastien Jodogne # Date 1443612211 -7200 # Node ID 4a5c79e31b6030ddd7b009f45c25aa9fb4564099 # Parent 61ce8147f30dc97e2d55a9f6e9ef5df363459fdb# Parent 87a606265de826f6a285f7d8f1b5eb453511ce9e integration mainline->db-changes diff -r 61ce8147f30d -r 4a5c79e31b60 CMakeLists.txt --- a/CMakeLists.txt Wed Feb 11 10:40:08 2015 +0100 +++ b/CMakeLists.txt Wed Sep 30 13:23:31 2015 +0200 @@ -5,6 +5,14 @@ # Version of the build, should always be "mainline" except in release branches set(ORTHANC_VERSION "mainline") +# Version of the database schema. History: +# * Orthanc 0.1.0 -> Orthanc 0.3.0 = no versioning +# * Orthanc 0.3.1 = version 2 +# * Orthanc 0.4.0 -> Orthanc 0.7.2 = version 3 +# * Orthanc 0.7.3 -> Orthanc 0.8.4 = version 4 +# * Orthanc 0.8.5 -> mainline = version 5 +set(ORTHANC_DATABASE_VERSION 5) + ##################################################################### ## CMake parameters tunable at the command line @@ -14,12 +22,14 @@ SET(STATIC_BUILD OFF CACHE BOOL "Static build of the third-party libraries (necessary for Windows)") SET(STANDALONE_BUILD ON CACHE BOOL "Standalone build (all the resources are embedded, necessary for releases)") SET(ENABLE_SSL ON CACHE BOOL "Include support for SSL") -SET(BUILD_CLIENT_LIBRARY ON CACHE BOOL "Build the client library") SET(DCMTK_DICTIONARY_DIR "" CACHE PATH "Directory containing the DCMTK dictionaries \"dicom.dic\" and \"private.dic\" (only when using system version of DCMTK)") SET(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages") SET(UNIT_TESTS_WITH_HTTP_CONNEXIONS ON CACHE BOOL "Allow unit tests to make HTTP requests") +SET(ENABLE_GOOGLE_LOG OFF CACHE BOOL "Enable Google Log (otherwise, an internal logger is used)") SET(ENABLE_JPEG ON CACHE BOOL "Enable JPEG decompression") SET(ENABLE_JPEG_LOSSLESS ON CACHE BOOL "Enable JPEG-LS (Lossless) decompression") +SET(ENABLE_PLUGINS ON CACHE BOOL "Enable plugins") +SET(BUILD_SERVE_FOLDERS ON CACHE BOOL "Build the ServeFolders plugin") # Advanced parameters to fine-tune linking against system libraries SET(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp") @@ -30,7 +40,8 @@ SET(USE_SYSTEM_LUA ON CACHE BOOL "Use the system version of Lua") SET(USE_SYSTEM_DCMTK ON CACHE BOOL "Use the system version of DCMTK") SET(USE_SYSTEM_BOOST ON CACHE BOOL "Use the system version of Boost") -SET(USE_SYSTEM_LIBPNG ON CACHE BOOL "Use the system version of LibPng") +SET(USE_SYSTEM_LIBPNG ON CACHE BOOL "Use the system version of libpng") +SET(USE_SYSTEM_LIBJPEG ON CACHE BOOL "Use the system version of libjpeg") SET(USE_SYSTEM_CURL ON CACHE BOOL "Use the system version of LibCurl") SET(USE_SYSTEM_OPENSSL ON CACHE BOOL "Use the system version of OpenSSL") SET(USE_SYSTEM_ZLIB ON CACHE BOOL "Use the system version of ZLib") @@ -41,9 +52,13 @@ # Distribution-specific settings SET(USE_GTEST_DEBIAN_SOURCE_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)") +SET(SYSTEM_MONGOOSE_USE_CALLBACKS ON CACHE BOOL "The system version of Mongoose uses callbacks (version >= 3.7)") +SET(USE_BOOST_ICONV ON CACHE BOOL "Use iconv instead of wconv (Windows only)") + mark_as_advanced(USE_GTEST_DEBIAN_SOURCE_PACKAGE) -SET(SYSTEM_MONGOOSE_USE_CALLBACKS ON CACHE BOOL "The system version of Mongoose uses callbacks (version >= 3.7)") mark_as_advanced(SYSTEM_MONGOOSE_USE_CALLBACKS) +mark_as_advanced(USE_BOOST_ICONV) +mark_as_advanced(USE_PUGIXML) # Path to the root folder of the Orthanc distribution set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR}) @@ -52,6 +67,7 @@ include(CheckIncludeFiles) include(CheckIncludeFileCXX) include(CheckLibraryExists) +include(FindPythonInterp) include(${CMAKE_SOURCE_DIR}/Resources/CMake/AutoGeneratedCode.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/DownloadPackage.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/Compiler.cmake) @@ -66,12 +82,13 @@ set(ORTHANC_CORE_SOURCES Core/Cache/MemoryCache.cpp + Core/Cache/SharedArchive.cpp Core/ChunkedBuffer.cpp - Core/Compression/BufferCompressor.cpp + Core/Compression/DeflateBaseCompressor.cpp + Core/Compression/GzipCompressor.cpp + Core/Compression/HierarchicalZipWriter.cpp + Core/Compression/ZipWriter.cpp Core/Compression/ZlibCompressor.cpp - Core/Compression/ZipWriter.cpp - Core/Compression/HierarchicalZipWriter.cpp - Core/OrthancException.cpp Core/DicomFormat/DicomArray.cpp Core/DicomFormat/DicomMap.cpp Core/DicomFormat/DicomTag.cpp @@ -81,34 +98,39 @@ Core/Enumerations.cpp Core/FileStorage/FilesystemStorage.cpp Core/FileStorage/StorageAccessor.cpp - Core/FileStorage/CompressedFileStorageAccessor.cpp - Core/FileStorage/FileStorageAccessor.cpp Core/HttpClient.cpp + Core/HttpServer/BufferHttpSender.cpp Core/HttpServer/EmbeddedResourceHttpHandler.cpp Core/HttpServer/FilesystemHttpHandler.cpp - Core/HttpServer/HttpHandler.cpp + Core/HttpServer/HttpToolbox.cpp Core/HttpServer/HttpOutput.cpp + Core/HttpServer/StringHttpOutput.cpp Core/HttpServer/MongooseServer.cpp Core/HttpServer/HttpFileSender.cpp Core/HttpServer/FilesystemHttpSender.cpp + Core/HttpServer/HttpStreamTranscoder.cpp + Core/Logging.cpp Core/RestApi/RestApiCall.cpp Core/RestApi/RestApiGetCall.cpp Core/RestApi/RestApiHierarchy.cpp Core/RestApi/RestApiPath.cpp Core/RestApi/RestApiOutput.cpp Core/RestApi/RestApi.cpp - Core/MultiThreading/ArrayFilledByThreads.cpp Core/MultiThreading/BagOfRunnablesBySteps.cpp Core/MultiThreading/Mutex.cpp Core/MultiThreading/ReaderWriterLock.cpp Core/MultiThreading/Semaphore.cpp Core/MultiThreading/SharedMessageQueue.cpp - Core/MultiThreading/ThreadedCommandProcessor.cpp - Core/ImageFormats/ImageAccessor.cpp - Core/ImageFormats/ImageBuffer.cpp - Core/ImageFormats/ImageProcessing.cpp - Core/ImageFormats/PngReader.cpp - Core/ImageFormats/PngWriter.cpp + Core/Images/Font.cpp + Core/Images/FontRegistry.cpp + Core/Images/ImageAccessor.cpp + Core/Images/ImageBuffer.cpp + Core/Images/ImageProcessing.cpp + Core/Images/JpegErrorManager.cpp + Core/Images/JpegReader.cpp + Core/Images/JpegWriter.cpp + Core/Images/PngReader.cpp + Core/Images/PngWriter.cpp Core/SQLite/Connection.cpp Core/SQLite/FunctionContext.cpp Core/SQLite/Statement.cpp @@ -119,17 +141,6 @@ Core/Uuid.cpp Core/Lua/LuaContext.cpp Core/Lua/LuaFunctionCall.cpp - - OrthancCppClient/OrthancConnection.cpp - OrthancCppClient/Study.cpp - OrthancCppClient/Series.cpp - OrthancCppClient/Instance.cpp - OrthancCppClient/Patient.cpp - - Plugins/Engine/SharedLibrary.cpp - Plugins/Engine/PluginsManager.cpp - Plugins/Engine/OrthancPlugins.cpp - Plugins/Engine/OrthancPluginDatabase.cpp ) @@ -166,6 +177,11 @@ OrthancServer/OrthancFindRequestHandler.cpp OrthancServer/OrthancMoveRequestHandler.cpp OrthancServer/ExportedResource.cpp + OrthancServer/ResourceFinder.cpp + OrthancServer/DicomFindQuery.cpp + OrthancServer/QueryRetrieveHandler.cpp + OrthancServer/LuaScripting.cpp + OrthancServer/OrthancHttpHandler.cpp # From "lua-scripting" branch OrthancServer/DicomInstanceToStore.cpp @@ -185,7 +201,7 @@ UnitTestsSources/FileStorageTests.cpp UnitTestsSources/FromDcmtkTests.cpp UnitTestsSources/MemoryCacheTests.cpp - UnitTestsSources/PngTests.cpp + UnitTestsSources/ImageTests.cpp UnitTestsSources/RestApiTests.cpp UnitTestsSources/SQLiteTests.cpp UnitTestsSources/SQLiteChromiumTests.cpp @@ -197,10 +213,26 @@ UnitTestsSources/UnitTestsMain.cpp UnitTestsSources/ImageProcessingTests.cpp UnitTestsSources/JpegLosslessTests.cpp - UnitTestsSources/PluginsTests.cpp + UnitTestsSources/StreamTests.cpp ) +if (ENABLE_PLUGINS) + list(APPEND ORTHANC_SERVER_SOURCES + Plugins/Engine/OrthancPluginDatabase.cpp + Plugins/Engine/OrthancPlugins.cpp + Plugins/Engine/PluginsEnumerations.cpp + Plugins/Engine/PluginsErrorDictionary.cpp + Plugins/Engine/PluginsManager.cpp + Plugins/Engine/SharedLibrary.cpp + ) + + list(APPEND ORTHANC_UNIT_TESTS_SOURCES + UnitTestsSources/PluginsTests.cpp + ) +endif() + + set(ORTHANC_EMBEDDED_FILES PREPARE_DATABASE ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/PrepareDatabase.sql UPGRADE_DATABASE_3_TO_4 ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/Upgrade3To4.sql @@ -208,6 +240,7 @@ CONFIGURATION_SAMPLE ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Configuration.json DICOM_CONFORMANCE_STATEMENT ${CMAKE_CURRENT_SOURCE_DIR}/Resources/DicomConformanceStatement.txt LUA_TOOLBOX ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Toolbox.lua + FONT_UBUNTU_MONO_BOLD_16 ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Fonts/UbuntuMonoBold-16.json ) @@ -216,32 +249,28 @@ ## Inclusion of third-party dependencies ##################################################################### -# Configuration of the standalone builds -if (CMAKE_CROSSCOMPILING) - # Cross-compilation implies the standalone build - SET(STANDALONE_BUILD ON) +if (ENABLE_GOOGLE_LOG) + include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleLogConfiguration.cmake) endif() -# Prepare the third-party dependencies -SET(THIRD_PARTY_SOURCES - ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/md5/md5.c - ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/base64/base64.cpp - ) +include(${CMAKE_SOURCE_DIR}/Resources/CMake/JsonCppConfiguration.cmake) +include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibCurlConfiguration.cmake) +include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibPngConfiguration.cmake) +include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibJpegConfiguration.cmake) +include(${CMAKE_SOURCE_DIR}/Resources/CMake/LuaConfiguration.cmake) +include(${CMAKE_SOURCE_DIR}/Resources/CMake/MongooseConfiguration.cmake) +include(${CMAKE_SOURCE_DIR}/Resources/CMake/PugixmlConfiguration.cmake) +include(${CMAKE_SOURCE_DIR}/Resources/CMake/SQLiteConfiguration.cmake) +include(${CMAKE_SOURCE_DIR}/Resources/CMake/ZlibConfiguration.cmake) -include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleLogConfiguration.cmake) +# These are the two most heavyweight dependencies. We put them as the +# last includes to quickly spot problems when configuring static +# builds. include(${CMAKE_SOURCE_DIR}/Resources/CMake/BoostConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/DcmtkConfiguration.cmake) -include(${CMAKE_SOURCE_DIR}/Resources/CMake/MongooseConfiguration.cmake) -include(${CMAKE_SOURCE_DIR}/Resources/CMake/ZlibConfiguration.cmake) -include(${CMAKE_SOURCE_DIR}/Resources/CMake/SQLiteConfiguration.cmake) -include(${CMAKE_SOURCE_DIR}/Resources/CMake/JsonCppConfiguration.cmake) -include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibPngConfiguration.cmake) -include(${CMAKE_SOURCE_DIR}/Resources/CMake/LuaConfiguration.cmake) -include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibCurlConfiguration.cmake) -include(${CMAKE_SOURCE_DIR}/Resources/CMake/PugixmlConfiguration.cmake) -if (${ENABLE_SSL}) +if (ENABLE_SSL) add_definitions(-DORTHANC_SSL_ENABLED=1) include(${CMAKE_SOURCE_DIR}/Resources/CMake/OpenSslConfiguration.cmake) else() @@ -263,12 +292,19 @@ endif() +if (ENABLE_PLUGINS) + add_definitions(-DORTHANC_PLUGINS_ENABLED=1) +else() + add_definitions(-DORTHANC_PLUGINS_ENABLED=0) +endif() + + ##################################################################### ## Autogeneration of files ##################################################################### -if (${STANDALONE_BUILD}) +if (STANDALONE_BUILD) # We embed all the resources in the binaries for standalone builds add_definitions(-DORTHANC_STANDALONE=1) EmbedResources( @@ -286,6 +322,22 @@ ) endif() +if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + execute_process( + COMMAND + ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py + ${ORTHANC_VERSION} Orthanc Orthanc.exe "Lightweight, RESTful DICOM server for medical imaging" + ERROR_VARIABLE Failure + OUTPUT_FILE ${AUTOGENERATED_DIR}/Orthanc.rc + ) + + if (Failure) + message(FATAL_ERROR "Error while computing the version information: ${Failure}") + endif() + + list(APPEND ORTHANC_RESOURCES ${AUTOGENERATED_DIR}/Orthanc.rc) +endif() + ##################################################################### @@ -293,7 +345,7 @@ ##################################################################### # Setup precompiled headers for Microsoft Visual Studio -if (${MSVC}) +if (MSVC) add_definitions(-DORTHANC_USE_PRECOMPILED_HEADERS=1) ADD_VISUAL_STUDIO_PRECOMPILED_HEADERS( @@ -309,6 +361,8 @@ add_definitions( -DORTHANC_VERSION="${ORTHANC_VERSION}" + -DORTHANC_DATABASE_VERSION=${ORTHANC_DATABASE_VERSION} + -DORTHANC_ENABLE_LOGGING=1 ) list(LENGTH OPENSSL_SOURCES OPENSSL_SOURCES_LENGTH) @@ -318,13 +372,31 @@ add_library(CoreLibrary STATIC + ${ORTHANC_CORE_SOURCES} ${AUTOGENERATED_SOURCES} - ${THIRD_PARTY_SOURCES} + + ${BOOST_SOURCES} ${CURL_SOURCES} - ${ORTHANC_CORE_SOURCES} + ${GOOGLE_LOG_SOURCES} + ${JSONCPP_SOURCES} + ${LIBPNG_SOURCES} + ${LIBJPEG_SOURCES} + ${LUA_SOURCES} + ${MONGOOSE_SOURCES} + ${PUGIXML_SOURCES} + ${SQLITE_SOURCES} + ${ZLIB_SOURCES} + + ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/md5/md5.c + ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/base64/base64.cpp + + # This is the minizip distribution to create ZIP files using zlib + ${ORTHANC_ROOT}/Resources/ThirdParty/minizip/ioapi.c + ${ORTHANC_ROOT}/Resources/ThirdParty/minizip/zip.c ) + ##################################################################### ## Build the Orthanc server ##################################################################### @@ -340,9 +412,10 @@ add_executable(Orthanc OrthancServer/main.cpp + ${ORTHANC_RESOURCES} ) -target_link_libraries(Orthanc ServerLibrary CoreLibrary ${STATIC_LUA} ${STATIC_GOOGLE_LOG}) +target_link_libraries(Orthanc ServerLibrary CoreLibrary ${DCMTK_LIBRARIES}) if (${OPENSSL_SOURCES_LENGTH} GREATER 0) target_link_libraries(Orthanc OpenSSL) @@ -365,13 +438,16 @@ add_definitions(-DUNIT_TESTS_WITH_HTTP_CONNEXIONS=0) endif() -add_definitions(-DORTHANC_BUILD_UNIT_TESTS=1) +add_definitions( + -DORTHANC_BUILD_UNIT_TESTS=1 + ) + include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleTestConfiguration.cmake) add_executable(UnitTests ${GTEST_SOURCES} ${ORTHANC_UNIT_TESTS_SOURCES} ) -target_link_libraries(UnitTests ServerLibrary CoreLibrary ${STATIC_LUA} ${STATIC_GOOGLE_LOG}) +target_link_libraries(UnitTests ServerLibrary CoreLibrary ${DCMTK_LIBRARIES}) if (${OPENSSL_SOURCES_LENGTH} GREATER 0) target_link_libraries(UnitTests OpenSSL) @@ -380,112 +456,47 @@ ##################################################################### -## Create the standalone DLL containing the Orthanc Client API +## Build the "ServeFolders" plugin ##################################################################### -if (BUILD_CLIENT_LIBRARY) - include_directories(${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/Laaw) - - if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") - if (CMAKE_CROSSCOMPILING) - # Remove the default "lib" prefix from "libOrthancClient.dll" if cross-compiling - set(CMAKE_SHARED_LIBRARY_PREFIX "") +if (ENABLE_PLUGINS AND BUILD_SERVE_FOLDERS) + execute_process( + COMMAND + ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py + ${ORTHANC_VERSION} ServeFolders ServeFolders.dll "Orthanc plugin to serve additional folders" + ERROR_VARIABLE Failure + OUTPUT_FILE ${AUTOGENERATED_DIR}/ServeFolders.rc + ) - if (${CMAKE_SIZEOF_VOID_P} EQUAL 4) - set(ORTHANC_CPP_CLIENT_AUX ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.def) - elseif (${CMAKE_SIZEOF_VOID_P} EQUAL 8) - set(ORTHANC_CPP_CLIENT_AUX ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.def) - else() - message(FATAL_ERROR "Support your platform here") - endif() - else() - # Nothing to do if using Visual Studio - endif() - - if (${CMAKE_SIZEOF_VOID_P} EQUAL 4) - set(CMAKE_SHARED_LIBRARY_SUFFIX "_Windows32.dll") - list(APPEND ORTHANC_CPP_CLIENT_AUX ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.rc) - elseif (${CMAKE_SIZEOF_VOID_P} EQUAL 8) - set(CMAKE_SHARED_LIBRARY_SUFFIX "_Windows64.dll") - list(APPEND ORTHANC_CPP_CLIENT_AUX ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.rc) - else() - message(FATAL_ERROR "Support your platform here") - endif() - - else() - set(ORTHANC_CPP_CLIENT_AUX ${OPENSSL_SOURCES}) + if (Failure) + message(FATAL_ERROR "Error while computing the version information: ${Failure}") endif() - add_library(OrthancClient SHARED - ${ORTHANC_ROOT}/OrthancCppClient/OrthancCppClient.cpp - ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/SharedLibrary.cpp - ${ORTHANC_ROOT}/Resources/ThirdParty/md5/md5.c - ${ORTHANC_ROOT}/Resources/ThirdParty/base64/base64.cpp - ${ORTHANC_CPP_CLIENT_AUX} - ${THIRD_PARTY_SOURCES} - ${CURL_SOURCES} - ${GOOGLE_LOG_SOURCES} + add_definitions(-DSERVE_FOLDERS_VERSION="${ORTHANC_VERSION}") + + include_directories(${CMAKE_SOURCE_DIR}/Plugins/Include) + + add_library(ServeFolders SHARED + ${BOOST_SOURCES} + ${JSONCPP_SOURCES} + Plugins/Samples/ServeFolders/Plugin.cpp + ${AUTOGENERATED_DIR}/ServeFolders.rc ) - if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR - ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD") - set_target_properties(OrthancClient - PROPERTIES LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined -Wl,--as-needed -Wl,--version-script=${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/Laaw/VersionScript.map" - ) - target_link_libraries(OrthancClient pthread) - - elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") - target_link_libraries(OrthancClient OpenSSL ws2_32) - - if (CMAKE_CROSSCOMPILING) - set_target_properties(OrthancClient - PROPERTIES LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++" - ) - endif() - - elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") - # TODO - target_link_libraries(OrthancClient pthread) - - else() - message(FATAL_ERROR "Support your platform here") - endif() - - - # Set the version of the "Orthanc Client" shared library - file(STRINGS - ${CMAKE_SOURCE_DIR}/OrthancCppClient/SharedLibrary/Product.json - ORTHANC_CLIENT_VERSION_TMP - REGEX "^[ \t]*\"Version\"[ \t]*") - - string(REGEX REPLACE "^.*\"([0-9]+)\\.([0-9]+)\\.([0-9]+)\"" "\\1.\\2" - ORTHANC_CLIENT_VERSION ${ORTHANC_CLIENT_VERSION_TMP}) - - message("Setting the version of the library to ${ORTHANC_CLIENT_VERSION}") - - set_target_properties(OrthancClient PROPERTIES - VERSION ${ORTHANC_CLIENT_VERSION} - SOVERSION ${ORTHANC_CLIENT_VERSION}) - - - install( - TARGETS OrthancClient - RUNTIME DESTINATION lib # Destination for Windows - LIBRARY DESTINATION lib # Destination for Linux + set_target_properties( + ServeFolders PROPERTIES + VERSION ${ORTHANC_VERSION} + SOVERSION ${ORTHANC_VERSION} ) install( - FILES - ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h - ${ORTHANC_ROOT}/Plugins/Include/OrthancCPlugin.h - ${ORTHANC_ROOT}/Plugins/Include/OrthancCDatabasePlugin.h - ${ORTHANC_ROOT}/Plugins/Include/OrthancCppDatabasePlugin.h - DESTINATION include/orthanc + TARGETS ServeFolders + RUNTIME DESTINATION lib # Destination for Windows + LIBRARY DESTINATION share/orthanc/plugins # Destination for Linux ) endif() - ##################################################################### ## Generate the documentation if Doxygen is present @@ -519,31 +530,28 @@ DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/OrthancPluginDocumentation/doc/ DESTINATION share/doc/orthanc/OrthancPlugin ) - - if (BUILD_CLIENT_LIBRARY) - configure_file( - ${CMAKE_SOURCE_DIR}/Resources/OrthancClient.doxygen - ${CMAKE_CURRENT_BINARY_DIR}/OrthancClient.doxygen - @ONLY) - - add_custom_command(TARGET OrthancClient - POST_BUILD - COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancClient.doxygen - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Generating client documentation with Doxygen" VERBATIM - ) - - install( - DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/OrthancClientDocumentation/doc/ - DESTINATION share/doc/orthanc/OrthancClient - ) - endif() - else() message("Doxygen not found. The documentation will not be built.") endif() + +##################################################################### +## Install the plugin SDK +##################################################################### + +if (ENABLE_PLUGINS) + install( + FILES + Plugins/Include/orthanc/OrthancCPlugin.h + Plugins/Include/orthanc/OrthancCDatabasePlugin.h + Plugins/Include/orthanc/OrthancCppDatabasePlugin.h + DESTINATION include/orthanc + ) +endif() + + + ##################################################################### ## Prepare the "uninstall" target ## http://www.cmake.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Cache/MemoryCache.cpp --- a/Core/Cache/MemoryCache.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Cache/MemoryCache.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -33,9 +33,7 @@ #include "../PrecompiledHeaders.h" #include "MemoryCache.h" -#include // This fixes a problem in glog for recent - // releases of MinGW -#include +#include "../Logging.h" namespace Orthanc { diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Cache/SharedArchive.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Cache/SharedArchive.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,134 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "SharedArchive.h" + + +#include "../Uuid.h" + + +namespace Orthanc +{ + void SharedArchive::RemoveInternal(const std::string& id) + { + Archive::iterator it = archive_.find(id); + + if (it != archive_.end()) + { + delete it->second; + archive_.erase(it); + } + } + + + SharedArchive::Accessor::Accessor(SharedArchive& that, + const std::string& id) : + lock_(that.mutex_) + { + Archive::iterator it = that.archive_.find(id); + + if (it == that.archive_.end()) + { + throw OrthancException(ErrorCode_InexistentItem); + } + else + { + that.lru_.MakeMostRecent(id); + item_ = it->second; + } + } + + + SharedArchive::SharedArchive(size_t maxSize) : + maxSize_(maxSize) + { + if (maxSize == 0) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + SharedArchive::~SharedArchive() + { + for (Archive::iterator it = archive_.begin(); + it != archive_.end(); ++it) + { + delete it->second; + } + } + + + std::string SharedArchive::Add(IDynamicObject* obj) + { + boost::mutex::scoped_lock lock(mutex_); + + if (archive_.size() == maxSize_) + { + // The quota has been reached, remove the oldest element + std::string oldest = lru_.RemoveOldest(); + RemoveInternal(oldest); + } + + std::string id = Toolbox::GenerateUuid(); + RemoveInternal(id); // Should never be useful because of UUID + archive_[id] = obj; + lru_.Add(id); + + return id; + } + + + void SharedArchive::Remove(const std::string& id) + { + boost::mutex::scoped_lock lock(mutex_); + RemoveInternal(id); + lru_.Invalidate(id); + } + + + void SharedArchive::List(std::list& items) + { + items.clear(); + + boost::mutex::scoped_lock lock(mutex_); + + for (Archive::const_iterator it = archive_.begin(); + it != archive_.end(); ++it) + { + items.push_back(it->first); + } + } +} + + diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Cache/SharedArchive.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Cache/SharedArchive.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,85 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "LeastRecentlyUsedIndex.h" +#include "../IDynamicObject.h" + +#include +#include + +namespace Orthanc +{ + class SharedArchive : public boost::noncopyable + { + private: + typedef std::map Archive; + + size_t maxSize_; + boost::mutex mutex_; + Archive archive_; + Orthanc::LeastRecentlyUsedIndex lru_; + + void RemoveInternal(const std::string& id); + + public: + class Accessor : public boost::noncopyable + { + private: + boost::mutex::scoped_lock lock_; + IDynamicObject* item_; + + public: + Accessor(SharedArchive& that, + const std::string& id); + + IDynamicObject& GetItem() const + { + return *item_; + } + }; + + + SharedArchive(size_t maxSize); + + ~SharedArchive(); + + std::string Add(IDynamicObject* obj); // Takes the ownership + + void Remove(const std::string& id); + + void List(std::list& items); + }; +} + + diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Compression/BufferCompressor.cpp --- a/Core/Compression/BufferCompressor.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeaders.h" -#include "BufferCompressor.h" - -namespace Orthanc -{ - void BufferCompressor::Compress(std::string& output, - const std::vector& input) - { - if (input.size() > 0) - Compress(output, &input[0], input.size()); - else - Compress(output, NULL, 0); - } - - void BufferCompressor::Uncompress(std::string& output, - const std::vector& input) - { - if (input.size() > 0) - Uncompress(output, &input[0], input.size()); - else - Uncompress(output, NULL, 0); - } - - void BufferCompressor::Compress(std::string& output, - const std::string& input) - { - if (input.size() > 0) - Compress(output, &input[0], input.size()); - else - Compress(output, NULL, 0); - } - - void BufferCompressor::Uncompress(std::string& output, - const std::string& input) - { - if (input.size() > 0) - Uncompress(output, &input[0], input.size()); - else - Uncompress(output, NULL, 0); - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Compression/BufferCompressor.h --- a/Core/Compression/BufferCompressor.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include -#include -#include -#include - -namespace Orthanc -{ - class BufferCompressor - { - public: - virtual ~BufferCompressor() - { - } - - virtual void Compress(std::string& compressed, - const void* uncompressed, - size_t uncompressedSize) = 0; - - virtual void Uncompress(std::string& uncompressed, - const void* compressed, - size_t compressedSize) = 0; - - virtual void Compress(std::string& compressed, - const std::vector& uncompressed); - - virtual void Uncompress(std::string& uncompressed, - const std::vector& compressed); - - virtual void Compress(std::string& compressed, - const std::string& uncompressed); - - virtual void Uncompress(std::string& uncompressed, - const std::string& compressed); - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Compression/DeflateBaseCompressor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Compression/DeflateBaseCompressor.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,75 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "DeflateBaseCompressor.h" + +#include "../OrthancException.h" +#include "../Logging.h" + +#include + +namespace Orthanc +{ + void DeflateBaseCompressor::SetCompressionLevel(uint8_t level) + { + if (level >= 10) + { + LOG(ERROR) << "Zlib compression level must be between 0 (no compression) and 9 (highest compression)"; + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + compressionLevel_ = level; + } + + + uint64_t DeflateBaseCompressor::ReadUncompressedSizePrefix(const void* compressed, + size_t compressedSize) + { + if (compressedSize == 0) + { + return 0; + } + + if (compressedSize < sizeof(uint64_t)) + { + LOG(ERROR) << "The compressed buffer is ill-formed"; + throw OrthancException(ErrorCode_CorruptedFile); + } + + uint64_t size; + memcpy(&size, compressed, sizeof(uint64_t)); + + return size; + } + +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Compression/DeflateBaseCompressor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Compression/DeflateBaseCompressor.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,75 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IBufferCompressor.h" + +#include + +namespace Orthanc +{ + class DeflateBaseCompressor : public IBufferCompressor + { + private: + uint8_t compressionLevel_; + bool prefixWithUncompressedSize_; + + protected: + uint64_t ReadUncompressedSizePrefix(const void* compressed, + size_t compressedSize); + + public: + DeflateBaseCompressor() : + compressionLevel_(6), + prefixWithUncompressedSize_(false) + { + } + + void SetCompressionLevel(uint8_t level); + + void SetPrefixWithUncompressedSize(bool prefix) + { + prefixWithUncompressedSize_ = prefix; + } + + bool HasPrefixWithUncompressedSize() const + { + return prefixWithUncompressedSize_; + } + + uint8_t GetCompressionLevel() const + { + return compressionLevel_; + } + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Compression/GzipCompressor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Compression/GzipCompressor.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,277 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "GzipCompressor.h" + +#include +#include +#include + +#include "../OrthancException.h" +#include "../Logging.h" + +namespace Orthanc +{ + uint64_t GzipCompressor::GuessUncompressedSize(const void* compressed, + size_t compressedSize) + { + /** + * "Is there a way to find out the size of the original file which + * is inside a GZIP file? [...] There is no truly reliable way, + * other than gunzipping the stream. You do not need to save the + * result of the decompression, so you can determine the size by + * simply reading and decoding the entire file without taking up + * space with the decompressed result. + * + * There is an unreliable way to determine the uncompressed size, + * which is to look at the last four bytes of the gzip file, which + * is the uncompressed length of that entry modulo 232 in little + * endian order. + * + * It is unreliable because a) the uncompressed data may be longer + * than 2^32 bytes, and b) the gzip file may consist of multiple + * gzip streams, in which case you would find the length of only + * the last of those streams. + * + * If you are in control of the source of the gzip files, you know + * that they consist of single gzip streams, and you know that + * they are less than 2^32 bytes uncompressed, then and only then + * can you use those last four bytes with confidence." + * + * http://stackoverflow.com/a/9727599/881731 + **/ + + if (compressedSize < 4) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + const uint8_t* p = reinterpret_cast(compressed) + compressedSize - 4; + + return ((static_cast(p[0]) << 0) + + (static_cast(p[1]) << 8) + + (static_cast(p[2]) << 16) + + (static_cast(p[3]) << 24)); + } + + + + void GzipCompressor::Compress(std::string& compressed, + const void* uncompressed, + size_t uncompressedSize) + { + uLongf compressedSize = compressBound(uncompressedSize) + 1024 /* security margin */; + if (compressedSize == 0) + { + compressedSize = 1; + } + + uint8_t* target; + if (HasPrefixWithUncompressedSize()) + { + compressed.resize(compressedSize + sizeof(uint64_t)); + target = reinterpret_cast(&compressed[0]) + sizeof(uint64_t); + } + else + { + compressed.resize(compressedSize); + target = reinterpret_cast(&compressed[0]); + } + + z_stream stream; + memset(&stream, 0, sizeof(stream)); + + stream.next_in = const_cast(reinterpret_cast(uncompressed)); + stream.next_out = reinterpret_cast(target); + + stream.avail_in = static_cast(uncompressedSize); + stream.avail_out = static_cast(compressedSize); + + // Ensure no overflow (if the buffer is too large for the current archicture) + if (static_cast(stream.avail_in) != uncompressedSize || + static_cast(stream.avail_out) != compressedSize) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + // Initialize the compression engine + int error = deflateInit2(&stream, + GetCompressionLevel(), + Z_DEFLATED, + MAX_WBITS + 16, // ask for gzip output + 8, // default memory level + Z_DEFAULT_STRATEGY); + + if (error != Z_OK) + { + // Cannot initialize zlib + compressed.clear(); + throw OrthancException(ErrorCode_InternalError); + } + + // Compress the input buffer + error = deflate(&stream, Z_FINISH); + + if (error != Z_STREAM_END) + { + deflateEnd(&stream); + compressed.clear(); + + switch (error) + { + case Z_MEM_ERROR: + throw OrthancException(ErrorCode_NotEnoughMemory); + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + size_t size = stream.total_out; + + if (deflateEnd(&stream) != Z_OK) + { + throw OrthancException(ErrorCode_InternalError); + } + + // The compression was successful + if (HasPrefixWithUncompressedSize()) + { + uint64_t s = static_cast(uncompressedSize); + memcpy(&compressed[0], &s, sizeof(uint64_t)); + compressed.resize(size + sizeof(uint64_t)); + } + else + { + compressed.resize(size); + } + } + + + void GzipCompressor::Uncompress(std::string& uncompressed, + const void* compressed, + size_t compressedSize) + { + uint64_t uncompressedSize; + const uint8_t* source = reinterpret_cast(compressed); + + if (HasPrefixWithUncompressedSize()) + { + uncompressedSize = ReadUncompressedSizePrefix(compressed, compressedSize); + source += sizeof(uint64_t); + compressedSize -= sizeof(uint64_t); + } + else + { + uncompressedSize = GuessUncompressedSize(compressed, compressedSize); + } + + try + { + uncompressed.resize(static_cast(uncompressedSize)); + } + catch (...) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + z_stream stream; + memset(&stream, 0, sizeof(stream)); + + char dummy = '\0'; // zlib does not like NULL output buffers (even if the uncompressed data is empty) + stream.next_in = const_cast(source); + stream.next_out = reinterpret_cast(uncompressedSize == 0 ? &dummy : &uncompressed[0]); + + stream.avail_in = static_cast(compressedSize); + stream.avail_out = static_cast(uncompressedSize); + + // Ensure no overflow (if the buffer is too large for the current archicture) + if (static_cast(stream.avail_in) != compressedSize || + static_cast(stream.avail_out) != uncompressedSize) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + // Initialize the compression engine + int error = inflateInit2(&stream, + MAX_WBITS + 16); // this is a gzip input + + if (error != Z_OK) + { + // Cannot initialize zlib + uncompressed.clear(); + throw OrthancException(ErrorCode_InternalError); + } + + // Uncompress the input buffer + error = inflate(&stream, Z_FINISH); + + if (error != Z_STREAM_END) + { + inflateEnd(&stream); + uncompressed.clear(); + + switch (error) + { + case Z_MEM_ERROR: + throw OrthancException(ErrorCode_NotEnoughMemory); + + case Z_BUF_ERROR: + case Z_NEED_DICT: + throw OrthancException(ErrorCode_BadFileFormat); + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + size_t size = stream.total_out; + + if (inflateEnd(&stream) != Z_OK) + { + uncompressed.clear(); + throw OrthancException(ErrorCode_InternalError); + } + + if (size != uncompressedSize) + { + uncompressed.clear(); + + // The uncompressed size was not that properly guess, presumably + // because of a file size over 4GB. Should fallback to + // stream-based decompression. + LOG(ERROR) << "The uncompressed size of a gzip-encoded buffer was not properly guessed"; + throw OrthancException(ErrorCode_NotImplemented); + } + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Compression/GzipCompressor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Compression/GzipCompressor.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,59 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "DeflateBaseCompressor.h" + +namespace Orthanc +{ + class GzipCompressor : public DeflateBaseCompressor + { + private: + uint64_t GuessUncompressedSize(const void* compressed, + size_t compressedSize); + + public: + GzipCompressor() + { + SetPrefixWithUncompressedSize(false); + } + + virtual void Compress(std::string& compressed, + const void* uncompressed, + size_t uncompressedSize); + + virtual void Uncompress(std::string& uncompressed, + const void* compressed, + size_t compressedSize); + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Compression/HierarchicalZipWriter.cpp --- a/Core/Compression/HierarchicalZipWriter.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Compression/HierarchicalZipWriter.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -53,7 +53,7 @@ if (c == '^') c = ' '; - if (c < 128 && + if (c <= 127 && c >= 0) { if (isspace(c)) diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Compression/IBufferCompressor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Compression/IBufferCompressor.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,73 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include +#include + +namespace Orthanc +{ + class IBufferCompressor : public boost::noncopyable + { + public: + virtual ~IBufferCompressor() + { + } + + virtual void Compress(std::string& compressed, + const void* uncompressed, + size_t uncompressedSize) = 0; + + virtual void Uncompress(std::string& uncompressed, + const void* compressed, + size_t compressedSize) = 0; + + static void Compress(std::string& compressed, + IBufferCompressor& compressor, + const std::string& uncompressed) + { + compressor.Compress(compressed, + uncompressed.size() == 0 ? NULL : uncompressed.c_str(), + uncompressed.size()); + } + + static void Uncompress(std::string& uncompressed, + IBufferCompressor& compressor, + const std::string& compressed) + { + compressor.Uncompress(uncompressed, + compressed.size() == 0 ? NULL : compressed.c_str(), + compressed.size()); + } + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Compression/ZipWriter.cpp --- a/Core/Compression/ZipWriter.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Compression/ZipWriter.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -44,6 +44,7 @@ #include "../../Resources/ThirdParty/minizip/zip.h" #include "../OrthancException.h" +#include "../Logging.h" static void PrepareFileInfo(zip_fileinfo& zfi) @@ -122,7 +123,8 @@ if (path_.size() == 0) { - throw OrthancException("Please call SetOutputPath() before creating the file"); + LOG(ERROR) << "Please call SetOutputPath() before creating the file"; + throw OrthancException(ErrorCode_BadSequenceOfCalls); } hasFileInZip_ = false; @@ -165,7 +167,8 @@ { if (level >= 10) { - throw OrthancException("ZIP compression level must be between 0 (no compression) and 9 (highest compression"); + LOG(ERROR) << "ZIP compression level must be between 0 (no compression) and 9 (highest compression)"; + throw OrthancException(ErrorCode_ParameterOutOfRange); } Close(); @@ -224,7 +227,8 @@ { if (!hasFileInZip_) { - throw OrthancException("Call first OpenFile()"); + LOG(ERROR) << "Call first OpenFile()"; + throw OrthancException(ErrorCode_BadSequenceOfCalls); } const size_t maxBytesInAStep = std::numeric_limits::max(); diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Compression/ZlibCompressor.cpp --- a/Core/Compression/ZlibCompressor.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Compression/ZlibCompressor.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -33,24 +33,15 @@ #include "../PrecompiledHeaders.h" #include "ZlibCompressor.h" +#include "../OrthancException.h" +#include "../Logging.h" + #include #include #include -#include "../OrthancException.h" namespace Orthanc { - void ZlibCompressor::SetCompressionLevel(uint8_t level) - { - if (level >= 10) - { - throw OrthancException("Zlib compression level must be between 0 (no compression) and 9 (highest compression"); - } - - compressionLevel_ = level; - } - - void ZlibCompressor::Compress(std::string& compressed, const void* uncompressed, size_t uncompressedSize) @@ -61,25 +52,32 @@ return; } - uLongf compressedSize = compressBound(uncompressedSize); - compressed.resize(compressedSize + sizeof(size_t)); + uLongf compressedSize = compressBound(uncompressedSize) + 1024 /* security margin */; + if (compressedSize == 0) + { + compressedSize = 1; + } - int error = compress2 - (reinterpret_cast(&compressed[0]) + sizeof(size_t), - &compressedSize, - const_cast(static_cast(uncompressed)), - uncompressedSize, - compressionLevel_); - - memcpy(&compressed[0], &uncompressedSize, sizeof(size_t)); - - if (error == Z_OK) + uint8_t* target; + if (HasPrefixWithUncompressedSize()) { - compressed.resize(compressedSize + sizeof(size_t)); - return; + compressed.resize(compressedSize + sizeof(uint64_t)); + target = reinterpret_cast(&compressed[0]) + sizeof(uint64_t); } else { + compressed.resize(compressedSize); + target = reinterpret_cast(&compressed[0]); + } + + int error = compress2(target, + &compressedSize, + const_cast(static_cast(uncompressed)), + uncompressedSize, + GetCompressionLevel()); + + if (error != Z_OK) + { compressed.clear(); switch (error) @@ -91,6 +89,18 @@ throw OrthancException(ErrorCode_InternalError); } } + + // The compression was successful + if (HasPrefixWithUncompressedSize()) + { + uint64_t s = static_cast(uncompressedSize); + memcpy(&compressed[0], &s, sizeof(uint64_t)); + compressed.resize(compressedSize + sizeof(uint64_t)); + } + else + { + compressed.resize(compressedSize); + } } @@ -104,29 +114,29 @@ return; } - if (compressedSize < sizeof(size_t)) + if (!HasPrefixWithUncompressedSize()) { - throw OrthancException("Zlib: The compressed buffer is ill-formed"); + LOG(ERROR) << "Cannot guess the uncompressed size of a zlib-encoded buffer"; + throw OrthancException(ErrorCode_InternalError); } - size_t uncompressedLength; - memcpy(&uncompressedLength, compressed, sizeof(size_t)); + uint64_t uncompressedSize = ReadUncompressedSizePrefix(compressed, compressedSize); try { - uncompressed.resize(uncompressedLength); + uncompressed.resize(static_cast(uncompressedSize)); } catch (...) { - throw OrthancException("Zlib: Corrupted compressed buffer"); + throw OrthancException(ErrorCode_NotEnoughMemory); } - uLongf tmp = uncompressedLength; + uLongf tmp = static_cast(uncompressedSize); int error = uncompress (reinterpret_cast(&uncompressed[0]), &tmp, - reinterpret_cast(compressed) + sizeof(size_t), - compressedSize - sizeof(size_t)); + reinterpret_cast(compressed) + sizeof(uint64_t), + compressedSize - sizeof(uint64_t)); if (error != Z_OK) { @@ -135,7 +145,7 @@ switch (error) { case Z_DATA_ERROR: - throw OrthancException("Zlib: Corrupted or incomplete compressed buffer"); + throw OrthancException(ErrorCode_CorruptedFile); case Z_MEM_ERROR: throw OrthancException(ErrorCode_NotEnoughMemory); diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Compression/ZlibCompressor.h --- a/Core/Compression/ZlibCompressor.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Compression/ZlibCompressor.h Wed Sep 30 13:23:31 2015 +0200 @@ -32,29 +32,16 @@ #pragma once -#include "BufferCompressor.h" +#include "DeflateBaseCompressor.h" namespace Orthanc { - class ZlibCompressor : public BufferCompressor + class ZlibCompressor : public DeflateBaseCompressor { - private: - uint8_t compressionLevel_; - public: - using BufferCompressor::Compress; - using BufferCompressor::Uncompress; - ZlibCompressor() { - compressionLevel_ = 6; - } - - void SetCompressionLevel(uint8_t level); - - uint8_t GetCompressionLevel() const - { - return compressionLevel_; + SetPrefixWithUncompressedSize(true); } virtual void Compress(std::string& compressed, diff -r 61ce8147f30d -r 4a5c79e31b60 Core/DicomFormat/DicomMap.cpp --- a/Core/DicomFormat/DicomMap.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/DicomFormat/DicomMap.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -273,6 +273,16 @@ result.SetValue(DICOM_TAG_ACCESSION_NUMBER, ""); result.SetValue(DICOM_TAG_PATIENT_ID, ""); result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, ""); + + // These tags are considered as "main" by Orthanc, but are not in the Series module + result.Remove(DicomTag(0x0008, 0x0070)); // Manufacturer + result.Remove(DicomTag(0x0008, 0x1010)); // Station name + result.Remove(DicomTag(0x0018, 0x0024)); // Sequence name + result.Remove(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES); + result.Remove(DICOM_TAG_IMAGES_IN_ACQUISITION); + result.Remove(DICOM_TAG_NUMBER_OF_SLICES); + result.Remove(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS); + result.Remove(DICOM_TAG_NUMBER_OF_TIME_SLICES); } void DicomMap::SetupFindInstanceTemplate(DicomMap& result) @@ -406,4 +416,16 @@ DicomArray a(*this); a.Print(fp); } + + + void DicomMap::GetTags(std::set& tags) const + { + tags.clear(); + + for (Map::const_iterator it = map_.begin(); + it != map_.end(); ++it) + { + tags.insert(it->first); + } + } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/DicomFormat/DicomMap.h --- a/Core/DicomFormat/DicomMap.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/DicomFormat/DicomMap.h Wed Sep 30 13:23:31 2015 +0200 @@ -77,6 +77,11 @@ { Clear(); } + + size_t GetSize() const + { + return map_.size(); + } DicomMap* Clone() const; @@ -166,5 +171,7 @@ static void GetMainDicomTags(std::set& result); void Print(FILE* fp) const; + + void GetTags(std::set& tags) const; }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/DicomFormat/DicomTag.cpp --- a/Core/DicomFormat/DicomTag.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/DicomFormat/DicomTag.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -118,11 +118,10 @@ } - void DicomTag::GetTagsForModule(std::set& target, + void DicomTag::AddTagsForModule(std::set& target, DicomModule module) { // REFERENCE: 11_03pu.pdf, DICOM PS 3.3 2011 - Information Object Definitions - target.clear(); switch (module) { diff -r 61ce8147f30d -r 4a5c79e31b60 Core/DicomFormat/DicomTag.h --- a/Core/DicomFormat/DicomTag.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/DicomFormat/DicomTag.h Wed Sep 30 13:23:31 2015 +0200 @@ -84,7 +84,7 @@ friend std::ostream& operator<< (std::ostream& o, const DicomTag& tag); - static void GetTagsForModule(std::set& target, + static void AddTagsForModule(std::set& target, DicomModule module); bool IsIdentifier() const; @@ -106,8 +106,8 @@ static const DicomTag DICOM_TAG_NUMBER_OF_FRAMES(0x0028, 0x0008); static const DicomTag DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES(0x0018, 0x1090); static const DicomTag DICOM_TAG_IMAGES_IN_ACQUISITION(0x0020, 0x1002); - static const DicomTag DICOM_TAG_PATIENT_NAME(0x0010, 0x0010); + static const DicomTag DICOM_TAG_ENCAPSULATED_DOCUMENT(0x0042, 0x0011); // The following is used for "modify/anonymize" operations static const DicomTag DICOM_TAG_SOP_CLASS_UID(0x0008, 0x0016); @@ -134,4 +134,18 @@ static const DicomTag DICOM_TAG_PIXEL_REPRESENTATION(0x0028, 0x0103); static const DicomTag DICOM_TAG_PLANAR_CONFIGURATION(0x0028, 0x0006); static const DicomTag DICOM_TAG_PHOTOMETRIC_INTERPRETATION(0x0028, 0x0004); + + // Tags related to date and time + static const DicomTag DICOM_TAG_ACQUISITION_DATE(0x0008, 0x0022); + static const DicomTag DICOM_TAG_ACQUISITION_TIME(0x0008, 0x0032); + static const DicomTag DICOM_TAG_CONTENT_DATE(0x0008, 0x0023); + static const DicomTag DICOM_TAG_CONTENT_TIME(0x0008, 0x0033); + static const DicomTag DICOM_TAG_INSTANCE_CREATION_DATE(0x0008, 0x0012); + static const DicomTag DICOM_TAG_INSTANCE_CREATION_TIME(0x0008, 0x0013); + static const DicomTag DICOM_TAG_PATIENT_BIRTH_DATE(0x0010, 0x0030); + static const DicomTag DICOM_TAG_PATIENT_BIRTH_TIME(0x0010, 0x0032); + static const DicomTag DICOM_TAG_SERIES_DATE(0x0008, 0x0021); + static const DicomTag DICOM_TAG_SERIES_TIME(0x0008, 0x0031); + static const DicomTag DICOM_TAG_STUDY_DATE(0x0008, 0x0020); + static const DicomTag DICOM_TAG_STUDY_TIME(0x0008, 0x0030); } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Enumerations.cpp --- a/Core/Enumerations.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Enumerations.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -36,8 +36,294 @@ #include "OrthancException.h" #include "Toolbox.h" +#include + namespace Orthanc { + // This function is autogenerated by the script + // "Resources/GenerateErrorCodes.py" + const char* EnumerationToString(ErrorCode error) + { + if (error >= ErrorCode_START_PLUGINS) + { + return "Error encountered within some plugin"; + } + + switch (error) + { + case ErrorCode_InternalError: + return "Internal error"; + + case ErrorCode_Success: + return "Success"; + + case ErrorCode_Plugin: + return "Error encountered within the plugin engine"; + + case ErrorCode_NotImplemented: + return "Not implemented yet"; + + case ErrorCode_ParameterOutOfRange: + return "Parameter out of range"; + + case ErrorCode_NotEnoughMemory: + return "Not enough memory"; + + case ErrorCode_BadParameterType: + return "Bad type for a parameter"; + + case ErrorCode_BadSequenceOfCalls: + return "Bad sequence of calls"; + + case ErrorCode_InexistentItem: + return "Accessing an inexistent item"; + + case ErrorCode_BadRequest: + return "Bad request"; + + case ErrorCode_NetworkProtocol: + return "Error in the network protocol"; + + case ErrorCode_SystemCommand: + return "Error while calling a system command"; + + case ErrorCode_Database: + return "Error with the database engine"; + + case ErrorCode_UriSyntax: + return "Badly formatted URI"; + + case ErrorCode_InexistentFile: + return "Inexistent file"; + + case ErrorCode_CannotWriteFile: + return "Cannot write to file"; + + case ErrorCode_BadFileFormat: + return "Bad file format"; + + case ErrorCode_Timeout: + return "Timeout"; + + case ErrorCode_UnknownResource: + return "Unknown resource"; + + case ErrorCode_IncompatibleDatabaseVersion: + return "Incompatible version of the database"; + + case ErrorCode_FullStorage: + return "The file storage is full"; + + case ErrorCode_CorruptedFile: + return "Corrupted file (e.g. inconsistent MD5 hash)"; + + case ErrorCode_InexistentTag: + return "Inexistent tag"; + + case ErrorCode_ReadOnly: + return "Cannot modify a read-only data structure"; + + case ErrorCode_IncompatibleImageFormat: + return "Incompatible format of the images"; + + case ErrorCode_IncompatibleImageSize: + return "Incompatible size of the images"; + + case ErrorCode_SharedLibrary: + return "Error while using a shared library (plugin)"; + + case ErrorCode_UnknownPluginService: + return "Plugin invoking an unknown service"; + + case ErrorCode_UnknownDicomTag: + return "Unknown DICOM tag"; + + case ErrorCode_BadJson: + return "Cannot parse a JSON document"; + + case ErrorCode_Unauthorized: + return "Bad credentials were provided to an HTTP request"; + + case ErrorCode_BadFont: + return "Badly formatted font file"; + + case ErrorCode_DatabasePlugin: + return "The plugin implementing a custom database back-end does not fulfill the proper interface"; + + case ErrorCode_StorageAreaPlugin: + return "Error in the plugin implementing a custom storage area"; + + case ErrorCode_SQLiteNotOpened: + return "SQLite: The database is not opened"; + + case ErrorCode_SQLiteAlreadyOpened: + return "SQLite: Connection is already open"; + + case ErrorCode_SQLiteCannotOpen: + return "SQLite: Unable to open the database"; + + case ErrorCode_SQLiteStatementAlreadyUsed: + return "SQLite: This cached statement is already being referred to"; + + case ErrorCode_SQLiteExecute: + return "SQLite: Cannot execute a command"; + + case ErrorCode_SQLiteRollbackWithoutTransaction: + return "SQLite: Rolling back a nonexistent transaction (have you called Begin()?)"; + + case ErrorCode_SQLiteCommitWithoutTransaction: + return "SQLite: Committing a nonexistent transaction"; + + case ErrorCode_SQLiteRegisterFunction: + return "SQLite: Unable to register a function"; + + case ErrorCode_SQLiteFlush: + return "SQLite: Unable to flush the database"; + + case ErrorCode_SQLiteCannotRun: + return "SQLite: Cannot run a cached statement"; + + case ErrorCode_SQLiteCannotStep: + return "SQLite: Cannot step over a cached statement"; + + case ErrorCode_SQLiteBindOutOfRange: + return "SQLite: Bing a value while out of range (serious error)"; + + case ErrorCode_SQLitePrepareStatement: + return "SQLite: Cannot prepare a cached statement"; + + case ErrorCode_SQLiteTransactionAlreadyStarted: + return "SQLite: Beginning the same transaction twice"; + + case ErrorCode_SQLiteTransactionCommit: + return "SQLite: Failure when committing the transaction"; + + case ErrorCode_SQLiteTransactionBegin: + return "SQLite: Cannot start a transaction"; + + case ErrorCode_DirectoryOverFile: + return "The directory to be created is already occupied by a regular file"; + + case ErrorCode_FileStorageCannotWrite: + return "Unable to create a subdirectory or a file in the file storage"; + + case ErrorCode_DirectoryExpected: + return "The specified path does not point to a directory"; + + case ErrorCode_HttpPortInUse: + return "The TCP port of the HTTP server is already in use"; + + case ErrorCode_DicomPortInUse: + return "The TCP port of the DICOM server is already in use"; + + case ErrorCode_BadHttpStatusInRest: + return "This HTTP status is not allowed in a REST API"; + + case ErrorCode_RegularFileExpected: + return "The specified path does not point to a regular file"; + + case ErrorCode_PathToExecutable: + return "Unable to get the path to the executable"; + + case ErrorCode_MakeDirectory: + return "Cannot create a directory"; + + case ErrorCode_BadApplicationEntityTitle: + return "An application entity title (AET) cannot be empty or be longer than 16 characters"; + + case ErrorCode_NoCFindHandler: + return "No request handler factory for DICOM C-FIND SCP"; + + case ErrorCode_NoCMoveHandler: + return "No request handler factory for DICOM C-MOVE SCP"; + + case ErrorCode_NoCStoreHandler: + return "No request handler factory for DICOM C-STORE SCP"; + + case ErrorCode_NoApplicationEntityFilter: + return "No application entity filter"; + + case ErrorCode_NoSopClassOrInstance: + return "DicomUserConnection: Unable to find the SOP class and instance"; + + case ErrorCode_NoPresentationContext: + return "DicomUserConnection: No acceptable presentation context for modality"; + + case ErrorCode_DicomFindUnavailable: + return "DicomUserConnection: The C-FIND command is not supported by the remote SCP"; + + case ErrorCode_DicomMoveUnavailable: + return "DicomUserConnection: The C-MOVE command is not supported by the remote SCP"; + + case ErrorCode_CannotStoreInstance: + return "Cannot store an instance"; + + case ErrorCode_CreateDicomNotString: + return "Only string values are supported when creating DICOM instances"; + + case ErrorCode_CreateDicomOverrideTag: + return "Trying to override a value inherited from a parent module"; + + case ErrorCode_CreateDicomUseContent: + return "Use \"Content\" to inject an image into a new DICOM instance"; + + case ErrorCode_CreateDicomNoPayload: + return "No payload is present for one instance in the series"; + + case ErrorCode_CreateDicomUseDataUriScheme: + return "The payload of the DICOM instance must be specified according to Data URI scheme"; + + case ErrorCode_CreateDicomBadParent: + return "Trying to attach a new DICOM instance to an inexistent resource"; + + case ErrorCode_CreateDicomParentIsInstance: + return "Trying to attach a new DICOM instance to an instance (must be a series, study or patient)"; + + case ErrorCode_CreateDicomParentEncoding: + return "Unable to get the encoding of the parent resource"; + + case ErrorCode_UnknownModality: + return "Unknown modality"; + + case ErrorCode_BadJobOrdering: + return "Bad ordering of filters in a job"; + + case ErrorCode_JsonToLuaTable: + return "Cannot convert the given JSON object to a Lua table"; + + case ErrorCode_CannotCreateLua: + return "Cannot create the Lua context"; + + case ErrorCode_CannotExecuteLua: + return "Cannot execute a Lua command"; + + case ErrorCode_LuaAlreadyExecuted: + return "Arguments cannot be pushed after the Lua function is executed"; + + case ErrorCode_LuaBadOutput: + return "The Lua function does not give the expected number of outputs"; + + case ErrorCode_NotLuaPredicate: + return "The Lua function is not a predicate (only true/false outputs allowed)"; + + case ErrorCode_LuaReturnsNoString: + return "The Lua function does not return a string"; + + case ErrorCode_StorageAreaAlreadyRegistered: + return "Another plugin has already registered a custom storage area"; + + case ErrorCode_DatabaseBackendAlreadyRegistered: + return "Another plugin has already registered a custom database back-end"; + + case ErrorCode_DatabaseNotInitialized: + return "Plugin trying to call the database during its initialization"; + + default: + return "Unknown error code"; + } + } + + const char* EnumerationToString(HttpMethod method) { switch (method) @@ -289,6 +575,9 @@ case Encoding_Cyrillic: return "Cyrillic"; + case Encoding_Windows1251: + return "Windows1251"; + case Encoding_Arabic: return "Arabic"; @@ -365,6 +654,53 @@ } + const char* EnumerationToString(RequestOrigin origin) + { + switch (origin) + { + case RequestOrigin_Unknown: + return "Unknown"; + + case RequestOrigin_DicomProtocol: + return "DicomProtocol"; + + case RequestOrigin_Http: + return "Http"; + + case RequestOrigin_Plugins: + return "Plugins"; + + case RequestOrigin_Lua: + return "Lua"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + const char* EnumerationToString(LogLevel level) + { + switch (level) + { + case LogLevel_Error: + return "ERROR"; + + case LogLevel_Warning: + return "WARNING"; + + case LogLevel_Info: + return "INFO"; + + case LogLevel_Trace: + return "TRACE"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + Encoding StringToEncoding(const char* encoding) { std::string s(encoding); @@ -410,6 +746,11 @@ return Encoding_Cyrillic; } + if (s == "WINDOWS1251") + { + return Encoding_Windows1251; + } + if (s == "ARABIC") { return Encoding_Arabic; @@ -485,6 +826,31 @@ } + LogLevel StringToLogLevel(const char *level) + { + if (strcmp(level, "ERROR") == 0) + { + return LogLevel_Error; + } + else if (strcmp(level, "WARNING") == 0) + { + return LogLevel_Warning; + } + else if (strcmp(level, "INFO") == 0) + { + return LogLevel_Info; + } + else if (strcmp(level, "TRACE") == 0) + { + return LogLevel_Trace; + } + else + { + throw OrthancException(ErrorCode_InternalError); + } + } + + unsigned int GetBytesPerPixel(PixelFormat format) { switch (format) @@ -617,4 +983,178 @@ return "application/octet-stream"; } } + + + const char* GetFileExtension(FileContentType type) + { + switch (type) + { + case FileContentType_Dicom: + return ".dcm"; + + case FileContentType_DicomAsJson: + return ".json"; + + default: + // Unknown file type + return ""; + } + } + + + ResourceType GetChildResourceType(ResourceType type) + { + switch (type) + { + case ResourceType_Patient: + return ResourceType_Study; + + case ResourceType_Study: + return ResourceType_Series; + + case ResourceType_Series: + return ResourceType_Instance; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + ResourceType GetParentResourceType(ResourceType type) + { + switch (type) + { + case ResourceType_Study: + return ResourceType_Patient; + + case ResourceType_Series: + return ResourceType_Study; + + case ResourceType_Instance: + return ResourceType_Series; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + DicomModule GetModule(ResourceType type) + { + switch (type) + { + case ResourceType_Patient: + return DicomModule_Patient; + + case ResourceType_Study: + return DicomModule_Study; + + case ResourceType_Series: + return DicomModule_Series; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + + const char* GetDicomSpecificCharacterSet(Encoding encoding) + { + // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ + switch (encoding) + { + case Encoding_Utf8: + case Encoding_Ascii: + return "ISO_IR 192"; + + case Encoding_Latin1: + return "ISO_IR 100"; + + case Encoding_Latin2: + return "ISO_IR 101"; + + case Encoding_Latin3: + return "ISO_IR 109"; + + case Encoding_Latin4: + return "ISO_IR 110"; + + case Encoding_Latin5: + return "ISO_IR 148"; + + case Encoding_Cyrillic: + return "ISO_IR 144"; + + case Encoding_Arabic: + return "ISO_IR 127"; + + case Encoding_Greek: + return "ISO_IR 126"; + + case Encoding_Hebrew: + return "ISO_IR 138"; + + case Encoding_Japanese: + return "ISO_IR 13"; + + case Encoding_Chinese: + return "GB18030"; + + case Encoding_Thai: + return "ISO_IR 166"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + // This function is autogenerated by the script + // "Resources/GenerateErrorCodes.py" + HttpStatus ConvertErrorCodeToHttpStatus(ErrorCode error) + { + switch (error) + { + case ErrorCode_Success: + return HttpStatus_200_Ok; + + case ErrorCode_ParameterOutOfRange: + return HttpStatus_400_BadRequest; + + case ErrorCode_BadParameterType: + return HttpStatus_400_BadRequest; + + case ErrorCode_InexistentItem: + return HttpStatus_404_NotFound; + + case ErrorCode_BadRequest: + return HttpStatus_400_BadRequest; + + case ErrorCode_UriSyntax: + return HttpStatus_400_BadRequest; + + case ErrorCode_InexistentFile: + return HttpStatus_404_NotFound; + + case ErrorCode_BadFileFormat: + return HttpStatus_400_BadRequest; + + case ErrorCode_UnknownResource: + return HttpStatus_404_NotFound; + + case ErrorCode_InexistentTag: + return HttpStatus_404_NotFound; + + case ErrorCode_BadJson: + return HttpStatus_400_BadRequest; + + case ErrorCode_Unauthorized: + return HttpStatus_401_Unauthorized; + + default: + return HttpStatus_500_InternalServerError; + } + } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Enumerations.h --- a/Core/Enumerations.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Enumerations.h Wed Sep 30 13:23:31 2015 +0200 @@ -32,8 +32,6 @@ #pragma once -#include - namespace Orthanc { enum Endianness @@ -43,45 +41,115 @@ Endianness_Little }; + // This enumeration is autogenerated by the script + // "Resources/GenerateErrorCodes.py" enum ErrorCode { - // Generic error codes - ErrorCode_Success, - ErrorCode_Custom, - ErrorCode_InternalError, - ErrorCode_NotImplemented, - ErrorCode_ParameterOutOfRange, - ErrorCode_NotEnoughMemory, - ErrorCode_BadParameterType, - ErrorCode_BadSequenceOfCalls, - ErrorCode_InexistentItem, - ErrorCode_BadRequest, - ErrorCode_NetworkProtocol, - ErrorCode_SystemCommand, - ErrorCode_Database, + ErrorCode_InternalError = -1 /*!< Internal error */, + ErrorCode_Success = 0 /*!< Success */, + ErrorCode_Plugin = 1 /*!< Error encountered within the plugin engine */, + ErrorCode_NotImplemented = 2 /*!< Not implemented yet */, + ErrorCode_ParameterOutOfRange = 3 /*!< Parameter out of range */, + ErrorCode_NotEnoughMemory = 4 /*!< Not enough memory */, + ErrorCode_BadParameterType = 5 /*!< Bad type for a parameter */, + ErrorCode_BadSequenceOfCalls = 6 /*!< Bad sequence of calls */, + ErrorCode_InexistentItem = 7 /*!< Accessing an inexistent item */, + ErrorCode_BadRequest = 8 /*!< Bad request */, + ErrorCode_NetworkProtocol = 9 /*!< Error in the network protocol */, + ErrorCode_SystemCommand = 10 /*!< Error while calling a system command */, + ErrorCode_Database = 11 /*!< Error with the database engine */, + ErrorCode_UriSyntax = 12 /*!< Badly formatted URI */, + ErrorCode_InexistentFile = 13 /*!< Inexistent file */, + ErrorCode_CannotWriteFile = 14 /*!< Cannot write to file */, + ErrorCode_BadFileFormat = 15 /*!< Bad file format */, + ErrorCode_Timeout = 16 /*!< Timeout */, + ErrorCode_UnknownResource = 17 /*!< Unknown resource */, + ErrorCode_IncompatibleDatabaseVersion = 18 /*!< Incompatible version of the database */, + ErrorCode_FullStorage = 19 /*!< The file storage is full */, + ErrorCode_CorruptedFile = 20 /*!< Corrupted file (e.g. inconsistent MD5 hash) */, + ErrorCode_InexistentTag = 21 /*!< Inexistent tag */, + ErrorCode_ReadOnly = 22 /*!< Cannot modify a read-only data structure */, + ErrorCode_IncompatibleImageFormat = 23 /*!< Incompatible format of the images */, + ErrorCode_IncompatibleImageSize = 24 /*!< Incompatible size of the images */, + ErrorCode_SharedLibrary = 25 /*!< Error while using a shared library (plugin) */, + ErrorCode_UnknownPluginService = 26 /*!< Plugin invoking an unknown service */, + ErrorCode_UnknownDicomTag = 27 /*!< Unknown DICOM tag */, + ErrorCode_BadJson = 28 /*!< Cannot parse a JSON document */, + ErrorCode_Unauthorized = 29 /*!< Bad credentials were provided to an HTTP request */, + ErrorCode_BadFont = 30 /*!< Badly formatted font file */, + ErrorCode_DatabasePlugin = 31 /*!< The plugin implementing a custom database back-end does not fulfill the proper interface */, + ErrorCode_StorageAreaPlugin = 32 /*!< Error in the plugin implementing a custom storage area */, + ErrorCode_SQLiteNotOpened = 1000 /*!< SQLite: The database is not opened */, + ErrorCode_SQLiteAlreadyOpened = 1001 /*!< SQLite: Connection is already open */, + ErrorCode_SQLiteCannotOpen = 1002 /*!< SQLite: Unable to open the database */, + ErrorCode_SQLiteStatementAlreadyUsed = 1003 /*!< SQLite: This cached statement is already being referred to */, + ErrorCode_SQLiteExecute = 1004 /*!< SQLite: Cannot execute a command */, + ErrorCode_SQLiteRollbackWithoutTransaction = 1005 /*!< SQLite: Rolling back a nonexistent transaction (have you called Begin()?) */, + ErrorCode_SQLiteCommitWithoutTransaction = 1006 /*!< SQLite: Committing a nonexistent transaction */, + ErrorCode_SQLiteRegisterFunction = 1007 /*!< SQLite: Unable to register a function */, + ErrorCode_SQLiteFlush = 1008 /*!< SQLite: Unable to flush the database */, + ErrorCode_SQLiteCannotRun = 1009 /*!< SQLite: Cannot run a cached statement */, + ErrorCode_SQLiteCannotStep = 1010 /*!< SQLite: Cannot step over a cached statement */, + ErrorCode_SQLiteBindOutOfRange = 1011 /*!< SQLite: Bing a value while out of range (serious error) */, + ErrorCode_SQLitePrepareStatement = 1012 /*!< SQLite: Cannot prepare a cached statement */, + ErrorCode_SQLiteTransactionAlreadyStarted = 1013 /*!< SQLite: Beginning the same transaction twice */, + ErrorCode_SQLiteTransactionCommit = 1014 /*!< SQLite: Failure when committing the transaction */, + ErrorCode_SQLiteTransactionBegin = 1015 /*!< SQLite: Cannot start a transaction */, + ErrorCode_DirectoryOverFile = 2000 /*!< The directory to be created is already occupied by a regular file */, + ErrorCode_FileStorageCannotWrite = 2001 /*!< Unable to create a subdirectory or a file in the file storage */, + ErrorCode_DirectoryExpected = 2002 /*!< The specified path does not point to a directory */, + ErrorCode_HttpPortInUse = 2003 /*!< The TCP port of the HTTP server is already in use */, + ErrorCode_DicomPortInUse = 2004 /*!< The TCP port of the DICOM server is already in use */, + ErrorCode_BadHttpStatusInRest = 2005 /*!< This HTTP status is not allowed in a REST API */, + ErrorCode_RegularFileExpected = 2006 /*!< The specified path does not point to a regular file */, + ErrorCode_PathToExecutable = 2007 /*!< Unable to get the path to the executable */, + ErrorCode_MakeDirectory = 2008 /*!< Cannot create a directory */, + ErrorCode_BadApplicationEntityTitle = 2009 /*!< An application entity title (AET) cannot be empty or be longer than 16 characters */, + ErrorCode_NoCFindHandler = 2010 /*!< No request handler factory for DICOM C-FIND SCP */, + ErrorCode_NoCMoveHandler = 2011 /*!< No request handler factory for DICOM C-MOVE SCP */, + ErrorCode_NoCStoreHandler = 2012 /*!< No request handler factory for DICOM C-STORE SCP */, + ErrorCode_NoApplicationEntityFilter = 2013 /*!< No application entity filter */, + ErrorCode_NoSopClassOrInstance = 2014 /*!< DicomUserConnection: Unable to find the SOP class and instance */, + ErrorCode_NoPresentationContext = 2015 /*!< DicomUserConnection: No acceptable presentation context for modality */, + ErrorCode_DicomFindUnavailable = 2016 /*!< DicomUserConnection: The C-FIND command is not supported by the remote SCP */, + ErrorCode_DicomMoveUnavailable = 2017 /*!< DicomUserConnection: The C-MOVE command is not supported by the remote SCP */, + ErrorCode_CannotStoreInstance = 2018 /*!< Cannot store an instance */, + ErrorCode_CreateDicomNotString = 2019 /*!< Only string values are supported when creating DICOM instances */, + ErrorCode_CreateDicomOverrideTag = 2020 /*!< Trying to override a value inherited from a parent module */, + ErrorCode_CreateDicomUseContent = 2021 /*!< Use \"Content\" to inject an image into a new DICOM instance */, + ErrorCode_CreateDicomNoPayload = 2022 /*!< No payload is present for one instance in the series */, + ErrorCode_CreateDicomUseDataUriScheme = 2023 /*!< The payload of the DICOM instance must be specified according to Data URI scheme */, + ErrorCode_CreateDicomBadParent = 2024 /*!< Trying to attach a new DICOM instance to an inexistent resource */, + ErrorCode_CreateDicomParentIsInstance = 2025 /*!< Trying to attach a new DICOM instance to an instance (must be a series, study or patient) */, + ErrorCode_CreateDicomParentEncoding = 2026 /*!< Unable to get the encoding of the parent resource */, + ErrorCode_UnknownModality = 2027 /*!< Unknown modality */, + ErrorCode_BadJobOrdering = 2028 /*!< Bad ordering of filters in a job */, + ErrorCode_JsonToLuaTable = 2029 /*!< Cannot convert the given JSON object to a Lua table */, + ErrorCode_CannotCreateLua = 2030 /*!< Cannot create the Lua context */, + ErrorCode_CannotExecuteLua = 2031 /*!< Cannot execute a Lua command */, + ErrorCode_LuaAlreadyExecuted = 2032 /*!< Arguments cannot be pushed after the Lua function is executed */, + ErrorCode_LuaBadOutput = 2033 /*!< The Lua function does not give the expected number of outputs */, + ErrorCode_NotLuaPredicate = 2034 /*!< The Lua function is not a predicate (only true/false outputs allowed) */, + ErrorCode_LuaReturnsNoString = 2035 /*!< The Lua function does not return a string */, + ErrorCode_StorageAreaAlreadyRegistered = 2036 /*!< Another plugin has already registered a custom storage area */, + ErrorCode_DatabaseBackendAlreadyRegistered = 2037 /*!< Another plugin has already registered a custom database back-end */, + ErrorCode_DatabaseNotInitialized = 2038 /*!< Plugin trying to call the database during its initialization */, + ErrorCode_START_PLUGINS = 1000000 + }; - // Specific error codes - ErrorCode_UriSyntax, - ErrorCode_InexistentFile, - ErrorCode_CannotWriteFile, - ErrorCode_BadFileFormat, - ErrorCode_Timeout, - ErrorCode_UnknownResource, - ErrorCode_IncompatibleDatabaseVersion, - ErrorCode_FullStorage, - ErrorCode_CorruptedFile, - ErrorCode_InexistentTag, - ErrorCode_ReadOnly, - ErrorCode_IncompatibleImageFormat, - ErrorCode_IncompatibleImageSize, - ErrorCode_SharedLibrary, - ErrorCode_Plugin + enum LogLevel + { + LogLevel_Error, + LogLevel_Warning, + LogLevel_Info, + LogLevel_Trace }; + /** * {summary}{The memory layout of the pixels (resp. voxels) of a 2D (resp. 3D) image.} **/ - enum LAAW_API PixelFormat + enum PixelFormat { /** * {summary}{Color image in RGB24 format.} @@ -120,7 +188,7 @@ /** * {summary}{The extraction mode specifies the way the values of the pixels are scaled when downloading a 2D image.} **/ - enum LAAW_API ImageExtractionMode + enum ImageExtractionMode { /** * {summary}{Rescaled to 8bpp.} @@ -232,6 +300,15 @@ }; + // https://en.wikipedia.org/wiki/HTTP_compression + enum HttpCompression + { + HttpCompression_None, + HttpCompression_Deflate, + HttpCompression_Gzip + }; + + // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ enum Encoding { @@ -243,6 +320,7 @@ Encoding_Latin4, Encoding_Latin5, // Turkish Encoding_Cyrillic, + Encoding_Windows1251, // Windows-1251 (commonly used for Cyrillic) Encoding_Arabic, Encoding_Greek, Encoding_Hebrew, @@ -283,6 +361,15 @@ DicomModule_Image }; + enum RequestOrigin + { + RequestOrigin_Unknown, + RequestOrigin_DicomProtocol, + RequestOrigin_Http, + RequestOrigin_Plugins, + RequestOrigin_Lua + }; + /** * WARNING: Do not change the explicit values in the enumerations @@ -292,8 +379,23 @@ enum CompressionType { + /** + * Buffer/file that is stored as-is, in a raw fashion, without + * compression. + **/ CompressionType_None = 1, - CompressionType_Zlib = 2 + + /** + * Buffer that is compressed using the "deflate" algorithm (RFC + * 1951), wrapped inside the zlib data format (RFC 1950), prefixed + * with a "uint64_t" (8 bytes) that encodes the size of the + * uncompressed buffer. If the compressed buffer is empty, its + * represents an empty uncompressed buffer. This format is + * internal to Orthanc. If the 8 first bytes are skipped AND the + * buffer is non-empty, the buffer is compatible with the + * "deflate" HTTP compression. + **/ + CompressionType_ZlibWithSize = 2 }; enum FileContentType @@ -318,6 +420,8 @@ }; + const char* EnumerationToString(ErrorCode code); + const char* EnumerationToString(HttpMethod method); const char* EnumerationToString(HttpStatus status); @@ -330,16 +434,34 @@ const char* EnumerationToString(PhotometricInterpretation photometric); + const char* EnumerationToString(LogLevel level); + + const char* EnumerationToString(RequestOrigin origin); + Encoding StringToEncoding(const char* encoding); ResourceType StringToResourceType(const char* type); ImageFormat StringToImageFormat(const char* format); + LogLevel StringToLogLevel(const char* format); + unsigned int GetBytesPerPixel(PixelFormat format); bool GetDicomEncoding(Encoding& encoding, const char* specificCharacterSet); const char* GetMimeType(FileContentType type); + + const char* GetFileExtension(FileContentType type); + + ResourceType GetChildResourceType(ResourceType type); + + ResourceType GetParentResourceType(ResourceType type); + + DicomModule GetModule(ResourceType type); + + const char* GetDicomSpecificCharacterSet(Encoding encoding); + + HttpStatus ConvertErrorCodeToHttpStatus(ErrorCode error); } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/FileStorage/CompressedFileStorageAccessor.cpp --- a/Core/FileStorage/CompressedFileStorageAccessor.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,180 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeaders.h" -#include "CompressedFileStorageAccessor.h" - -#include "../OrthancException.h" -#include "FileStorageAccessor.h" -#include "../HttpServer/BufferHttpSender.h" -#include "../Uuid.h" - -#include -#include - -namespace Orthanc -{ - FileInfo CompressedFileStorageAccessor::WriteInternal(const void* data, - size_t size, - FileContentType type) - { - std::string uuid = Toolbox::GenerateUuid(); - - std::string md5; - - if (storeMD5_) - { - Toolbox::ComputeMD5(md5, data, size); - } - - switch (compressionType_) - { - case CompressionType_None: - { - GetStorageArea().Create(uuid.c_str(), data, size, type); - return FileInfo(uuid, type, size, md5); - } - - case CompressionType_Zlib: - { - std::string compressed; - zlib_.Compress(compressed, data, size); - - std::string compressedMD5; - - if (storeMD5_) - { - Toolbox::ComputeMD5(compressedMD5, compressed); - } - - if (compressed.size() > 0) - { - GetStorageArea().Create(uuid.c_str(), &compressed[0], compressed.size(), type); - } - else - { - GetStorageArea().Create(uuid.c_str(), NULL, 0, type); - } - - return FileInfo(uuid, type, size, md5, - CompressionType_Zlib, compressed.size(), compressedMD5); - } - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - } - - - CompressedFileStorageAccessor::CompressedFileStorageAccessor() : - storage_(NULL), - compressionType_(CompressionType_None) - { - } - - - CompressedFileStorageAccessor::CompressedFileStorageAccessor(IStorageArea& storage) : - storage_(&storage), - compressionType_(CompressionType_None) - { - } - - - IStorageArea& CompressedFileStorageAccessor::GetStorageArea() - { - if (storage_ == NULL) - { - LOG(ERROR) << "No storage area is currently available"; - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - - return *storage_; - } - - - void CompressedFileStorageAccessor::Read(std::string& content, - const std::string& uuid, - FileContentType type) - { - switch (compressionType_) - { - case CompressionType_None: - GetStorageArea().Read(content, uuid, type); - break; - - case CompressionType_Zlib: - { - std::string compressed; - GetStorageArea().Read(compressed, uuid, type); - zlib_.Uncompress(content, compressed); - break; - } - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - } - - HttpFileSender* CompressedFileStorageAccessor::ConstructHttpFileSender(const std::string& uuid, - FileContentType type) - { - switch (compressionType_) - { - case CompressionType_None: - { - FileStorageAccessor uncompressedAccessor(GetStorageArea()); - return uncompressedAccessor.ConstructHttpFileSender(uuid, type); - } - - case CompressionType_Zlib: - { - std::string compressed; - GetStorageArea().Read(compressed, uuid, type); - - std::auto_ptr sender(new BufferHttpSender); - zlib_.Uncompress(sender->GetBuffer(), compressed); - - return sender.release(); - } - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - } - - - void CompressedFileStorageAccessor::Remove(const std::string& uuid, - FileContentType type) - { - GetStorageArea().Remove(uuid, type); - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/FileStorage/CompressedFileStorageAccessor.h --- a/Core/FileStorage/CompressedFileStorageAccessor.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IStorageArea.h" -#include "StorageAccessor.h" -#include "../Compression/ZlibCompressor.h" - -namespace Orthanc -{ - class CompressedFileStorageAccessor : public StorageAccessor - { - private: - IStorageArea* storage_; - ZlibCompressor zlib_; - CompressionType compressionType_; - - protected: - virtual FileInfo WriteInternal(const void* data, - size_t size, - FileContentType type); - - public: - CompressedFileStorageAccessor(); - - CompressedFileStorageAccessor(IStorageArea& storage); - - void SetStorageArea(IStorageArea& storage) - { - storage_ = &storage; - } - - bool HasStorageArea() const - { - return storage_ != NULL; - } - - IStorageArea& GetStorageArea(); - - void SetCompressionForNextOperations(CompressionType compression) - { - compressionType_ = compression; - } - - CompressionType GetCompressionForNextOperations() - { - return compressionType_; - } - - virtual void Read(std::string& content, - const std::string& uuid, - FileContentType type); - - virtual HttpFileSender* ConstructHttpFileSender(const std::string& uuid, - FileContentType type); - - virtual void Remove(const std::string& uuid, - FileContentType type); - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/FileStorage/FileStorageAccessor.cpp --- a/Core/FileStorage/FileStorageAccessor.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeaders.h" -#include "FileStorageAccessor.h" - -#include "../HttpServer/BufferHttpSender.h" -#include "../Uuid.h" - -#include -#include - -namespace Orthanc -{ - FileInfo FileStorageAccessor::WriteInternal(const void* data, - size_t size, - FileContentType type) - { - std::string md5; - - if (storeMD5_) - { - Toolbox::ComputeMD5(md5, data, size); - } - - std::string uuid = Toolbox::GenerateUuid(); - storage_.Create(uuid.c_str(), data, size, type); - - return FileInfo(uuid, type, size, md5); - } - - - HttpFileSender* FileStorageAccessor::ConstructHttpFileSender(const std::string& uuid, - FileContentType type) - { - std::auto_ptr sender(new BufferHttpSender); - - storage_.Read(sender->GetBuffer(), uuid, type); - - return sender.release(); - } - -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/FileStorage/FileStorageAccessor.h --- a/Core/FileStorage/FileStorageAccessor.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "StorageAccessor.h" -#include "IStorageArea.h" - -namespace Orthanc -{ - class FileStorageAccessor : public StorageAccessor - { - private: - IStorageArea& storage_; - - protected: - virtual FileInfo WriteInternal(const void* data, - size_t size, - FileContentType type); - - public: - FileStorageAccessor(IStorageArea& storage) : storage_(storage) - { - } - - virtual void Read(std::string& content, - const std::string& uuid, - FileContentType type) - { - storage_.Read(content, uuid, type); - } - - virtual HttpFileSender* ConstructHttpFileSender(const std::string& uuid, - FileContentType type); - - virtual void Remove(const std::string& uuid, - FileContentType type) - { - storage_.Remove(uuid, type); - } - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/FileStorage/FilesystemStorage.cpp --- a/Core/FileStorage/FilesystemStorage.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/FileStorage/FilesystemStorage.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -36,12 +36,13 @@ // http://stackoverflow.com/questions/1576272/storing-large-number-of-files-in-file-system // http://stackoverflow.com/questions/446358/storing-a-large-number-of-images +#include "../Logging.h" #include "../OrthancException.h" #include "../Toolbox.h" #include "../Uuid.h" #include -#include + static std::string ToString(const boost::filesystem::path& p) { @@ -82,7 +83,7 @@ //root_ = boost::filesystem::absolute(root).string(); root_ = root; - Toolbox::CreateDirectory(root); + Toolbox::MakeDirectory(root); } void FilesystemStorage::Create(const std::string& uuid, @@ -105,14 +106,14 @@ { if (!boost::filesystem::is_directory(path.parent_path())) { - throw OrthancException("The subdirectory to be created is already occupied by a regular file"); + throw OrthancException(ErrorCode_DirectoryOverFile); } } else { if (!boost::filesystem::create_directories(path.parent_path())) { - throw OrthancException("Unable to create a subdirectory in the file storage"); + throw OrthancException(ErrorCode_FileStorageCannotWrite); } } @@ -120,7 +121,7 @@ f.open(path, std::ofstream::out | std::ios::binary); if (!f.good()) { - throw OrthancException("Unable to create a new file in the file storage"); + throw OrthancException(ErrorCode_FileStorageCannotWrite); } if (size != 0) @@ -129,7 +130,7 @@ if (!f.good()) { f.close(); - throw OrthancException("Unable to write to the new file in the file storage"); + throw OrthancException(ErrorCode_FileStorageCannotWrite); } } @@ -212,7 +213,10 @@ void FilesystemStorage::Remove(const std::string& uuid, FileContentType /*type*/) { +#if ORTHANC_ENABLE_GOOGLE_LOG == 1 LOG(INFO) << "Deleting file " << uuid; +#endif + namespace fs = boost::filesystem; fs::path p = GetPath(uuid); diff -r 61ce8147f30d -r 4a5c79e31b60 Core/FileStorage/FilesystemStorage.h --- a/Core/FileStorage/FilesystemStorage.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/FileStorage/FilesystemStorage.h Wed Sep 30 13:23:31 2015 +0200 @@ -34,6 +34,7 @@ #include "IStorageArea.h" +#include #include #include diff -r 61ce8147f30d -r 4a5c79e31b60 Core/FileStorage/IStorageArea.h --- a/Core/FileStorage/IStorageArea.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/FileStorage/IStorageArea.h Wed Sep 30 13:23:31 2015 +0200 @@ -34,6 +34,7 @@ #include "../Enumerations.h" +#include #include namespace Orthanc diff -r 61ce8147f30d -r 4a5c79e31b60 Core/FileStorage/StorageAccessor.cpp --- a/Core/FileStorage/StorageAccessor.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/FileStorage/StorageAccessor.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -33,31 +33,126 @@ #include "../PrecompiledHeaders.h" #include "StorageAccessor.h" +#include "../Compression/ZlibCompressor.h" +#include "../OrthancException.h" +#include "../HttpServer/HttpStreamTranscoder.h" + namespace Orthanc { - FileInfo StorageAccessor::Write(const std::vector& content, - FileContentType type) + FileInfo StorageAccessor::Write(const void* data, + size_t size, + FileContentType type, + CompressionType compression, + bool storeMd5) { - if (content.size() == 0) + std::string uuid = Toolbox::GenerateUuid(); + + std::string md5; + + if (storeMd5) + { + Toolbox::ComputeMD5(md5, data, size); + } + + switch (compression) { - return WriteInternal(NULL, 0, type); - } - else - { - return WriteInternal(&content[0], content.size(), type); + case CompressionType_None: + { + area_.Create(uuid, data, size, type); + return FileInfo(uuid, type, size, md5); + } + + case CompressionType_ZlibWithSize: + { + ZlibCompressor zlib; + + std::string compressed; + zlib.Compress(compressed, data, size); + + std::string compressedMD5; + + if (storeMd5) + { + Toolbox::ComputeMD5(compressedMD5, compressed); + } + + if (compressed.size() > 0) + { + area_.Create(uuid, &compressed[0], compressed.size(), type); + } + else + { + area_.Create(uuid, NULL, 0, type); + } + + return FileInfo(uuid, type, size, md5, + CompressionType_ZlibWithSize, compressed.size(), compressedMD5); + } + + default: + throw OrthancException(ErrorCode_NotImplemented); } } - FileInfo StorageAccessor::Write(const std::string& content, - FileContentType type) + + void StorageAccessor::Read(std::string& content, + const FileInfo& info) { - if (content.size() == 0) + switch (info.GetCompressionType()) { - return WriteInternal(NULL, 0, type); + case CompressionType_None: + { + area_.Read(content, info.GetUuid(), info.GetContentType()); + break; + } + + case CompressionType_ZlibWithSize: + { + ZlibCompressor zlib; + + std::string compressed; + area_.Read(compressed, info.GetUuid(), info.GetContentType()); + IBufferCompressor::Uncompress(content, zlib, compressed); + break; + } + + default: + { + throw OrthancException(ErrorCode_NotImplemented); + } } - else - { - return WriteInternal(&content[0], content.size(), type); - } + + // TODO Check the validity of the uncompressed MD5? + } + + + void StorageAccessor::SetupSender(BufferHttpSender& sender, + const FileInfo& info) + { + area_.Read(sender.GetBuffer(), info.GetUuid(), info.GetContentType()); + sender.SetContentType(GetMimeType(info.GetContentType())); + sender.SetContentFilename(info.GetUuid() + std::string(GetFileExtension(info.GetContentType()))); + } + + + void StorageAccessor::AnswerFile(HttpOutput& output, + const FileInfo& info) + { + BufferHttpSender sender; + SetupSender(sender, info); + + HttpStreamTranscoder transcoder(sender, info.GetCompressionType()); + output.Answer(transcoder); + } + + + void StorageAccessor::AnswerFile(RestApiOutput& output, + const FileInfo& info) + { + BufferHttpSender sender; + SetupSender(sender, info); + + HttpStreamTranscoder transcoder(sender, info.GetCompressionType()); + output.AnswerStream(transcoder); } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/FileStorage/StorageAccessor.h --- a/Core/FileStorage/StorageAccessor.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/FileStorage/StorageAccessor.h Wed Sep 30 13:23:31 2015 +0200 @@ -32,8 +32,10 @@ #pragma once +#include "IStorageArea.h" #include "FileInfo.h" -#include "../HttpServer/HttpFileSender.h" +#include "../HttpServer/BufferHttpSender.h" +#include "../RestApi/RestApiOutput.h" #include #include @@ -44,54 +46,44 @@ { class StorageAccessor : boost::noncopyable { - protected: - bool storeMD5_; + private: + IStorageArea& area_; - virtual FileInfo WriteInternal(const void* data, - size_t size, - FileContentType type) = 0; + void SetupSender(BufferHttpSender& sender, + const FileInfo& info); public: - StorageAccessor() - { - storeMD5_ = true; - } - - virtual ~StorageAccessor() + StorageAccessor(IStorageArea& area) : area_(area) { } - void SetStoreMD5(bool storeMD5) - { - storeMD5_ = storeMD5; - } - - bool IsStoreMD5() const - { - return storeMD5_; - } - FileInfo Write(const void* data, size_t size, - FileContentType type) + FileContentType type, + CompressionType compression, + bool storeMd5); + + FileInfo Write(const std::string& data, + FileContentType type, + CompressionType compression, + bool storeMd5) { - return WriteInternal(data, size, type); + return Write((data.size() == 0 ? NULL : data.c_str()), + data.size(), type, compression, storeMd5); } - FileInfo Write(const std::vector& content, - FileContentType type); - - FileInfo Write(const std::string& content, - FileContentType type); + void Read(std::string& content, + const FileInfo& info); - virtual void Read(std::string& content, - const std::string& uuid, - FileContentType type) = 0; + void Remove(const FileInfo& info) + { + area_.Remove(info.GetUuid(), info.GetContentType()); + } - virtual void Remove(const std::string& uuid, - FileContentType type) = 0; + void AnswerFile(HttpOutput& output, + const FileInfo& info); - virtual HttpFileSender* ConstructHttpFileSender(const std::string& uuid, - FileContentType type) = 0; + void AnswerFile(RestApiOutput& output, + const FileInfo& info); }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpClient.cpp --- a/Core/HttpClient.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpClient.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -33,11 +33,46 @@ #include "PrecompiledHeaders.h" #include "HttpClient.h" -#include "../Core/Toolbox.h" -#include "../Core/OrthancException.h" +#include "Toolbox.h" +#include "OrthancException.h" +#include "Logging.h" #include #include +#include + + +static std::string globalCACertificates_; +static bool globalVerifyPeers_ = true; +static long globalTimeout_ = 0; + +extern "C" +{ + static CURLcode GetHttpStatus(CURLcode code, CURL* curl, long* status) + { + if (code == CURLE_OK) + { + code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status); + return code; + } + else + { + *status = 0; + return code; + } + } + + // This is a dummy wrapper function to suppress any OpenSSL-related + // problem in valgrind. Inlining is prevented. +#if defined(__GNUC__) || defined(__clang__) + __attribute__((noinline)) +#endif + static CURLcode OrthancHttpClientPerformSSL(CURL* curl, long* status) + { + return GetHttpStatus(curl_easy_perform(curl), curl, status); + } +} + namespace Orthanc @@ -49,11 +84,32 @@ }; + static void ThrowException(HttpStatus status) + { + switch (status) + { + case HttpStatus_400_BadRequest: + throw OrthancException(ErrorCode_BadRequest); + + case HttpStatus_401_Unauthorized: + throw OrthancException(ErrorCode_Unauthorized); + + case HttpStatus_404_NotFound: + throw OrthancException(ErrorCode_InexistentItem); + + default: + throw OrthancException(ErrorCode_NetworkProtocol); + } + } + + + static CURLcode CheckCode(CURLcode code) { if (code != CURLE_OK) { - throw OrthancException("libCURL error: " + std::string(curl_easy_strerror(code))); + LOG(ERROR) << "libCURL error: " + std::string(curl_easy_strerror(code)); + throw OrthancException(ErrorCode_NetworkProtocol); } return code; @@ -96,10 +152,6 @@ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADER, 0)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1)); -#if ORTHANC_SSL_ENABLED == 1 - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0)); -#endif - // This fixes the "longjmp causes uninitialized stack frame" crash // that happens on modern Linux versions. // http://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame @@ -109,7 +161,8 @@ method_ = HttpMethod_Get; lastStatus_ = HttpStatus_200_Ok; isVerbose_ = false; - timeout_ = 0; + timeout_ = globalTimeout_; + verifyPeers_ = globalVerifyPeers_; } @@ -163,6 +216,19 @@ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_URL, url_.c_str())); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &answer)); + // Setup HTTPS-related options +#if ORTHANC_SSL_ENABLED == 1 + if (IsHttpsVerifyPeers()) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CAINFO, GetHttpsCACertificates().c_str())); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 1)); + } + else + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0)); + } +#endif + // Reset the parameters from previous calls to Apply() CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, NULL)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 0L)); @@ -229,10 +295,10 @@ if (method_ == HttpMethod_Post || method_ == HttpMethod_Put) { - if (postData_.size() > 0) + if (body_.size() > 0) { - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, postData_.c_str())); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, postData_.size())); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, body_.c_str())); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, body_.size())); } else { @@ -243,10 +309,19 @@ // Do the actual request - CheckCode(curl_easy_perform(pimpl_->curl_)); + CURLcode code; + long status = 0; - long status; - CheckCode(curl_easy_getinfo(pimpl_->curl_, CURLINFO_RESPONSE_CODE, &status)); + if (boost::starts_with(url_, "https://")) + { + code = OrthancHttpClientPerformSSL(pimpl_->curl_, &status); + } + else + { + code = GetHttpStatus(curl_easy_perform(pimpl_->curl_), pimpl_->curl_, &status); + } + + CheckCode(code); if (status == 0) { @@ -284,13 +359,74 @@ } - void HttpClient::GlobalInitialize() + const std::string& HttpClient::GetHttpsCACertificates() const + { + if (caCertificates_.empty()) + { + return globalCACertificates_; + } + else + { + return caCertificates_; + } + } + + + void HttpClient::GlobalInitialize(bool httpsVerifyPeers, + const std::string& httpsVerifyCertificates) { + globalVerifyPeers_ = httpsVerifyPeers; + globalCACertificates_ = httpsVerifyCertificates; + +#if ORTHANC_SSL_ENABLED == 1 + if (httpsVerifyPeers) + { + if (globalCACertificates_.empty()) + { + LOG(WARNING) << "No certificates are provided to validate peers, " + << "set \"HttpsCACertificates\" if you need to do HTTPS requests"; + } + else + { + LOG(WARNING) << "HTTPS will use the CA certificates from this file: " << globalCACertificates_; + } + } + else + { + LOG(WARNING) << "The verification of the peers in HTTPS requests is disabled!"; + } +#endif + CheckCode(curl_global_init(CURL_GLOBAL_DEFAULT)); } + void HttpClient::GlobalFinalize() { curl_global_cleanup(); } + + + void HttpClient::SetDefaultTimeout(long timeout) + { + LOG(INFO) << "Setting the default timeout for HTTP client connections: " << timeout << " seconds"; + globalTimeout_ = timeout; + } + + + void HttpClient::ApplyAndThrowException(std::string& answer) + { + if (!Apply(answer)) + { + ThrowException(GetLastStatus()); + } + } + + void HttpClient::ApplyAndThrowException(Json::Value& answer) + { + if (!Apply(answer)) + { + ThrowException(GetLastStatus()); + } + } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpClient.h --- a/Core/HttpClient.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpClient.h Wed Sep 30 13:23:31 2015 +0200 @@ -32,7 +32,7 @@ #pragma once -#include "../Core/Enumerations.h" +#include "Enumerations.h" #include #include @@ -50,10 +50,12 @@ std::string credentials_; HttpMethod method_; HttpStatus lastStatus_; - std::string postData_; + std::string body_; // This only makes sense for POST and PUT requests bool isVerbose_; long timeout_; std::string proxy_; + bool verifyPeers_; + std::string caCertificates_; void Setup(); @@ -101,19 +103,19 @@ return timeout_; } - void SetPostData(const std::string& data) + void SetBody(const std::string& data) { - postData_ = data; + body_ = data; } - std::string& AccessPostData() + std::string& GetBody() { - return postData_; + return body_; } - const std::string& AccessPostData() const + const std::string& GetBody() const { - return postData_; + return body_; } void SetVerbose(bool isVerbose); @@ -140,8 +142,32 @@ proxy_ = proxy; } - static void GlobalInitialize(); + void SetHttpsVerifyPeers(bool verify) + { + verifyPeers_ = verify; + } + + bool IsHttpsVerifyPeers() const + { + return verifyPeers_; + } + + void SetHttpsCACertificates(const std::string& certificates) + { + caCertificates_ = certificates; + } + + const std::string& GetHttpsCACertificates() const; + + static void GlobalInitialize(bool httpsVerifyPeers, + const std::string& httpsCACertificates); static void GlobalFinalize(); + + static void SetDefaultTimeout(long timeout); + + void ApplyAndThrowException(std::string& answer); + + void ApplyAndThrowException(Json::Value& answer); }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/BufferHttpSender.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/BufferHttpSender.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,84 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + +#include "../PrecompiledHeaders.h" +#include "BufferHttpSender.h" + +#include "../OrthancException.h" + +#include + +namespace Orthanc +{ + BufferHttpSender::BufferHttpSender() : + position_(0), + chunkSize_(0), + currentChunkSize_(0) + { + } + + + bool BufferHttpSender::ReadNextChunk() + { + assert(position_ + currentChunkSize_ <= buffer_.size()); + + position_ += currentChunkSize_; + + if (position_ == buffer_.size()) + { + return false; + } + else + { + currentChunkSize_ = buffer_.size() - position_; + + if (chunkSize_ != 0 && + currentChunkSize_ > chunkSize_) + { + currentChunkSize_ = chunkSize_; + } + + return true; + } + } + + + const char* BufferHttpSender::GetChunkContent() + { + return buffer_.c_str() + position_; + } + + + size_t BufferHttpSender::GetChunkSize() + { + return currentChunkSize_; + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/BufferHttpSender.h --- a/Core/HttpServer/BufferHttpSender.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/BufferHttpSender.h Wed Sep 30 13:23:31 2015 +0200 @@ -38,25 +38,14 @@ class BufferHttpSender : public HttpFileSender { private: - std::string buffer_; - - protected: - virtual uint64_t GetFileSize() - { - return buffer_.size(); - } - - virtual bool SendData(HttpOutput& output) - { - if (buffer_.size()) - { - output.SendBody(&buffer_[0], buffer_.size()); - } - - return true; - } + std::string buffer_; + size_t position_; + size_t chunkSize_; + size_t currentChunkSize_; public: + BufferHttpSender(); + std::string& GetBuffer() { return buffer_; @@ -66,5 +55,28 @@ { return buffer_; } + + // This is for test purpose. If "chunkSize" is set to "0" (the + // default), the entire buffer is consumed at once. + void SetChunkSize(size_t chunkSize) + { + chunkSize_ = chunkSize; + } + + + /** + * Implementation of the IHttpStreamAnswer interface. + **/ + + virtual uint64_t GetContentLength() + { + return buffer_.size(); + } + + virtual bool ReadNextChunk(); + + virtual const char* GetChunkContent(); + + virtual size_t GetChunkSize(); }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/EmbeddedResourceHttpHandler.cpp --- a/Core/HttpServer/EmbeddedResourceHttpHandler.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/EmbeddedResourceHttpHandler.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -33,11 +33,11 @@ #include "../PrecompiledHeaders.h" #include "EmbeddedResourceHttpHandler.h" +#include "../Logging.h" #include "../OrthancException.h" #include "HttpOutput.h" #include -#include namespace Orthanc @@ -53,11 +53,15 @@ bool EmbeddedResourceHttpHandler::Handle( HttpOutput& output, + RequestOrigin /*origin*/, + const char* /*remoteIp*/, + const char* /*username*/, HttpMethod method, const UriComponents& uri, const Arguments& headers, - const Arguments& arguments, - const std::string&) + const GetArguments& arguments, + const char* /*bodyData*/, + size_t /*bodySize*/) { if (!Toolbox::IsChildUri(baseUri_, uri)) { @@ -80,7 +84,7 @@ size_t size = EmbeddedResources::GetDirectoryResourceSize(resourceId_, resourcePath.c_str()); output.SetContentType(contentType.c_str()); - output.SendBody(buffer, size); + output.Answer(buffer, size); } catch (OrthancException&) { diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/EmbeddedResourceHttpHandler.h --- a/Core/HttpServer/EmbeddedResourceHttpHandler.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/EmbeddedResourceHttpHandler.h Wed Sep 30 13:23:31 2015 +0200 @@ -32,14 +32,14 @@ #pragma once -#include "HttpHandler.h" +#include "IHttpHandler.h" #include // Autogenerated file #include namespace Orthanc { - class EmbeddedResourceHttpHandler : public HttpHandler + class EmbeddedResourceHttpHandler : public IHttpHandler { private: UriComponents baseUri_; @@ -52,10 +52,14 @@ virtual bool Handle( HttpOutput& output, + RequestOrigin origin, + const char* remoteIp, + const char* username, HttpMethod method, const UriComponents& uri, const Arguments& headers, - const Arguments& arguments, - const std::string&); + const GetArguments& arguments, + const char* /*bodyData*/, + size_t /*bodySize*/); }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/FilesystemHttpHandler.cpp --- a/Core/HttpServer/FilesystemHttpHandler.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/FilesystemHttpHandler.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -50,13 +50,12 @@ static void OutputDirectoryContent(HttpOutput& output, + const IHttpHandler::Arguments& headers, const UriComponents& uri, const boost::filesystem::path& p) { namespace fs = boost::filesystem; - output.SetContentType("text/html"); - std::string s; s += ""; s += " "; @@ -104,7 +103,8 @@ s += " "; s += ""; - output.SendBody(s); + output.SetContentType("text/html"); + output.Answer(s); } @@ -119,18 +119,22 @@ if (!fs::exists(pimpl_->root_) || !fs::is_directory(pimpl_->root_)) { - throw OrthancException("The path does not point to a directory"); + throw OrthancException(ErrorCode_DirectoryExpected); } } bool FilesystemHttpHandler::Handle( HttpOutput& output, + RequestOrigin /*origin*/, + const char* /*remoteIp*/, + const char* /*username*/, HttpMethod method, const UriComponents& uri, const Arguments& headers, - const Arguments& arguments, - const std::string&) + const GetArguments& arguments, + const char* /*bodyData*/, + size_t /*bodySize*/) { if (!Toolbox::IsChildUri(pimpl_->baseUri_, uri)) { @@ -154,15 +158,14 @@ if (fs::exists(p) && fs::is_regular_file(p)) { - FilesystemHttpSender(p).Send(output); - - //output.AnswerFileAutodetectContentType(p.string()); + FilesystemHttpSender sender(p); + output.Answer(sender); // TODO COMPRESSION } else if (listDirectoryContent_ && fs::exists(p) && fs::is_directory(p)) { - OutputDirectoryContent(output, uri, p); + OutputDirectoryContent(output, headers, uri, p); } else { diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/FilesystemHttpHandler.h --- a/Core/HttpServer/FilesystemHttpHandler.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/FilesystemHttpHandler.h Wed Sep 30 13:23:31 2015 +0200 @@ -32,13 +32,13 @@ #pragma once -#include "HttpHandler.h" +#include "IHttpHandler.h" #include namespace Orthanc { - class FilesystemHttpHandler : public HttpHandler + class FilesystemHttpHandler : public IHttpHandler { private: // PImpl idiom to avoid the inclusion of boost::filesystem @@ -54,11 +54,15 @@ virtual bool Handle( HttpOutput& output, + RequestOrigin origin, + const char* remoteIp, + const char* username, HttpMethod method, const UriComponents& uri, const Arguments& headers, - const Arguments& arguments, - const std::string&); + const GetArguments& arguments, + const char* /*bodyData*/, + size_t /*bodySize*/); bool IsListDirectoryContent() const { diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/FilesystemHttpSender.cpp --- a/Core/HttpServer/FilesystemHttpSender.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/FilesystemHttpSender.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -32,72 +32,45 @@ #include "../PrecompiledHeaders.h" #include "FilesystemHttpSender.h" -#include "../Toolbox.h" +#include "../OrthancException.h" -#include +static const size_t CHUNK_SIZE = 64 * 1024; // Use 64KB chunks namespace Orthanc { - void FilesystemHttpSender::Setup() + void FilesystemHttpSender::Initialize(const boost::filesystem::path& path) { - //SetDownloadFilename(path_.filename().string()); - -#if BOOST_HAS_FILESYSTEM_V3 == 1 - SetContentType(Toolbox::AutodetectMimeType(path_.filename().string())); -#else - SetContentType(Toolbox::AutodetectMimeType(path_.filename())); -#endif - } + SetContentFilename(path.filename().string()); + file_.open(path.string().c_str(), std::ifstream::binary); - uint64_t FilesystemHttpSender::GetFileSize() - { - return Toolbox::GetFileSize(path_.string()); - } - - bool FilesystemHttpSender::SendData(HttpOutput& output) - { - FILE* fp = fopen(path_.string().c_str(), "rb"); - if (!fp) + if (!file_.is_open()) { - return false; + throw OrthancException(ErrorCode_InexistentFile); } - std::vector buffer(1024 * 1024); // Chunks of 1MB - - for (;;) - { - size_t nbytes = fread(&buffer[0], 1, buffer.size(), fp); - if (nbytes == 0) - { - break; - } - else - { - output.SendBody(&buffer[0], nbytes); - } - } - - fclose(fp); - - return true; + file_.seekg(0, file_.end); + size_ = file_.tellg(); + file_.seekg(0, file_.beg); } - FilesystemHttpSender::FilesystemHttpSender(const char* path) - { - path_ = std::string(path); - Setup(); - } - FilesystemHttpSender::FilesystemHttpSender(const boost::filesystem::path& path) + bool FilesystemHttpSender::ReadNextChunk() { - path_ = path; - Setup(); - } + if (chunk_.size() == 0) + { + chunk_.resize(CHUNK_SIZE); + } + + file_.read(&chunk_[0], chunk_.size()); - FilesystemHttpSender::FilesystemHttpSender(const FilesystemStorage& storage, - const std::string& uuid) - { - path_ = storage.GetPath(uuid).string(); - Setup(); + if ((file_.flags() & std::istream::failbit) || + file_.gcount() < 0) + { + throw OrthancException(ErrorCode_CorruptedFile); + } + + chunkSize_ = file_.gcount(); + + return chunkSize_ > 0; } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/FilesystemHttpSender.h --- a/Core/HttpServer/FilesystemHttpSender.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/FilesystemHttpSender.h Wed Sep 30 13:23:31 2015 +0200 @@ -32,28 +32,59 @@ #pragma once #include "HttpFileSender.h" +#include "BufferHttpSender.h" #include "../FileStorage/FilesystemStorage.h" +#include + namespace Orthanc { class FilesystemHttpSender : public HttpFileSender { private: - boost::filesystem::path path_; - - void Setup(); + std::ifstream file_; + uint64_t size_; + std::string chunk_; + size_t chunkSize_; - protected: - virtual uint64_t GetFileSize(); - - virtual bool SendData(HttpOutput& output); + void Initialize(const boost::filesystem::path& path); public: - FilesystemHttpSender(const char* path); + FilesystemHttpSender(const std::string& path) + { + Initialize(path); + } - FilesystemHttpSender(const boost::filesystem::path& path); + FilesystemHttpSender(const boost::filesystem::path& path) + { + Initialize(path); + } FilesystemHttpSender(const FilesystemStorage& storage, - const std::string& uuid); + const std::string& uuid) + { + Initialize(storage.GetPath(uuid)); + } + + /** + * Implementation of the IHttpStreamAnswer interface. + **/ + + virtual uint64_t GetContentLength() + { + return size_; + } + + virtual bool ReadNextChunk(); + + virtual const char* GetChunkContent() + { + return chunk_.c_str(); + } + + virtual size_t GetChunkSize() + { + return chunkSize_; + } }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/HttpFileSender.cpp --- a/Core/HttpServer/HttpFileSender.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/HttpFileSender.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -34,34 +34,45 @@ #include "HttpFileSender.h" #include "../OrthancException.h" +#include "../Toolbox.h" #include namespace Orthanc { - void HttpFileSender::SendHeader(HttpOutput& output) + void HttpFileSender::SetContentFilename(const std::string& filename) { - if (contentType_.size() > 0) - { - output.SetContentType(contentType_.c_str()); - } + filename_ = filename; - if (downloadFilename_.size() > 0) + if (contentType_.empty()) { - output.SetContentFilename(downloadFilename_.c_str()); + contentType_ = Toolbox::AutodetectMimeType(filename); } - - output.SetContentLength(GetFileSize()); } - void HttpFileSender::Send(HttpOutput& output) + + bool HttpFileSender::HasContentFilename(std::string& filename) { - SendHeader(output); - - if (!SendData(output)) + if (filename_.empty()) + { + return false; + } + else { - throw OrthancException(ErrorCode_InternalError); - //output.SendHeader(HttpStatus_500_InternalServerError); + filename = filename_; + return true; + } + } + + std::string HttpFileSender::GetContentType() + { + if (contentType_.empty()) + { + return "application/octet-stream"; + } + else + { + return contentType_; } } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/HttpFileSender.h --- a/Core/HttpServer/HttpFileSender.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/HttpFileSender.h Wed Sep 30 13:23:31 2015 +0200 @@ -36,29 +36,13 @@ namespace Orthanc { - class HttpFileSender + class HttpFileSender : public IHttpStreamAnswer { private: std::string contentType_; - std::string downloadFilename_; - - void SendHeader(HttpOutput& output); - - protected: - virtual uint64_t GetFileSize() = 0; - - virtual bool SendData(HttpOutput& output) = 0; + std::string filename_; public: - virtual ~HttpFileSender() - { - } - - void ResetContentType() - { - contentType_.clear(); - } - void SetContentType(const std::string& contentType) { contentType_ = contentType; @@ -69,21 +53,26 @@ return contentType_; } - void ResetDownloadFilename() + void SetContentFilename(const std::string& filename); + + const std::string& GetContentFilename() const { - downloadFilename_.clear(); + return filename_; } - void SetDownloadFilename(const std::string& filename) + + /** + * Implementation of the IHttpStreamAnswer interface. + **/ + + virtual HttpCompression SetupHttpCompression(bool /*gzipAllowed*/, + bool /*deflateAllowed*/) { - downloadFilename_ = filename; + return HttpCompression_None; } - const std::string& GetDownloadFilename() const - { - return downloadFilename_; - } - - void Send(HttpOutput& output); + virtual bool HasContentFilename(std::string& filename); + + virtual std::string GetContentType(); }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/HttpHandler.cpp --- a/Core/HttpServer/HttpHandler.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,162 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeaders.h" -#include "HttpHandler.h" - -#include -#include - - -namespace Orthanc -{ - static void SplitGETNameValue(HttpHandler::Arguments& result, - const char* start, - const char* end) - { - std::string name, value; - - const char* equal = strchr(start, '='); - if (equal == NULL || equal >= end) - { - name = std::string(start, end - start); - //value = ""; - } - else - { - name = std::string(start, equal - start); - value = std::string(equal + 1, end); - } - - Toolbox::UrlDecode(name); - Toolbox::UrlDecode(value); - - result.insert(std::make_pair(name, value)); - } - - - void HttpHandler::ParseGetArguments(HttpHandler::Arguments& result, const char* query) - { - const char* pos = query; - - while (pos != NULL) - { - const char* ampersand = strchr(pos, '&'); - if (ampersand) - { - SplitGETNameValue(result, pos, ampersand); - pos = ampersand + 1; - } - else - { - // No more ampersand, this is the last argument - SplitGETNameValue(result, pos, pos + strlen(pos)); - pos = NULL; - } - } - } - - - void HttpHandler::ParseGetQuery(UriComponents& uri, - HttpHandler::Arguments& getArguments, - const char* query) - { - const char *questionMark = ::strchr(query, '?'); - if (questionMark == NULL) - { - // No question mark in the string - Toolbox::SplitUriComponents(uri, query); - getArguments.clear(); - } - else - { - Toolbox::SplitUriComponents(uri, std::string(query, questionMark)); - HttpHandler::ParseGetArguments(getArguments, questionMark + 1); - } - } - - - std::string HttpHandler::GetArgument(const Arguments& getArguments, - const std::string& name, - const std::string& defaultValue) - { - Arguments::const_iterator it = getArguments.find(name); - if (it == getArguments.end()) - { - return defaultValue; - } - else - { - return it->second; - } - } - - - - void HttpHandler::ParseCookies(HttpHandler::Arguments& result, - const HttpHandler::Arguments& httpHeaders) - { - result.clear(); - - HttpHandler::Arguments::const_iterator it = httpHeaders.find("cookie"); - if (it != httpHeaders.end()) - { - const std::string& cookies = it->second; - - size_t pos = 0; - while (pos != std::string::npos) - { - size_t nextSemicolon = cookies.find(";", pos); - std::string cookie; - - if (nextSemicolon == std::string::npos) - { - cookie = cookies.substr(pos); - pos = std::string::npos; - } - else - { - cookie = cookies.substr(pos, nextSemicolon - pos); - pos = nextSemicolon + 1; - } - - size_t equal = cookie.find("="); - if (equal != std::string::npos) - { - std::string name = Toolbox::StripSpaces(cookie.substr(0, equal)); - std::string value = Toolbox::StripSpaces(cookie.substr(equal + 1)); - result[name] = value; - } - } - } - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/HttpHandler.h --- a/Core/HttpServer/HttpHandler.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include -#include -#include -#include "../Toolbox.h" - -namespace Orthanc -{ - class HttpOutput; - - class HttpHandler - { - public: - typedef std::map Arguments; - - virtual ~HttpHandler() - { - } - - virtual bool Handle(HttpOutput& output, - HttpMethod method, - const UriComponents& uri, - const Arguments& headers, - const Arguments& getArguments, - const std::string& postData) = 0; - - static void ParseGetArguments(HttpHandler::Arguments& result, - const char* query); - - static void ParseGetQuery(UriComponents& uri, - HttpHandler::Arguments& getArguments, - const char* query); - - static std::string GetArgument(const Arguments& getArguments, - const std::string& name, - const std::string& defaultValue); - - static void ParseCookies(HttpHandler::Arguments& result, - const HttpHandler::Arguments& httpHeaders); - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/HttpOutput.cpp --- a/Core/HttpServer/HttpOutput.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/HttpOutput.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -33,13 +33,17 @@ #include "../PrecompiledHeaders.h" #include "HttpOutput.h" +#include "../Logging.h" +#include "../OrthancException.h" +#include "../Toolbox.h" +#include "../Compression/GzipCompressor.h" +#include "../Compression/ZlibCompressor.h" + #include #include #include -#include #include -#include "../OrthancException.h" -#include "../Toolbox.h" + namespace Orthanc { @@ -151,6 +155,11 @@ } } + if (state_ == State_WritingMultipart) + { + throw OrthancException(ErrorCode_InternalError); + } + if (state_ == State_WritingHeader) { // Send the HTTP header before writing the body @@ -206,6 +215,69 @@ } + void HttpOutput::StateMachine::CloseBody() + { + switch (state_) + { + case State_WritingHeader: + SetContentLength(0); + SendBody(NULL, 0); + break; + + case State_WritingBody: + if (!hasContentLength_ || + contentPosition_ == contentLength_) + { + state_ = State_Done; + } + else + { + LOG(ERROR) << "The body size has not reached what was declared with SetContentSize()"; + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + break; + + case State_WritingMultipart: + LOG(ERROR) << "Cannot invoke CloseBody() with multipart outputs"; + throw OrthancException(ErrorCode_BadSequenceOfCalls); + + case State_Done: + return; // Ignore + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + + HttpCompression HttpOutput::GetPreferredCompression(size_t bodySize) const + { +#if 0 + // TODO Do not compress small files? + if (bodySize < 512) + { + return HttpCompression_None; + } +#endif + + // Prefer "gzip" over "deflate" if the choice is offered + + if (isGzipAllowed_) + { + return HttpCompression_Gzip; + } + else if (isDeflateAllowed_) + { + return HttpCompression_Deflate; + } + else + { + return HttpCompression_None; + } + } + + void HttpOutput::SendMethodNotAllowed(const std::string& allowed) { stateMachine_.ClearHeaders(); @@ -215,7 +287,9 @@ } - void HttpOutput::SendStatus(HttpStatus status) + void HttpOutput::SendStatus(HttpStatus status, + const char* message, + size_t messageSize) { if (status == HttpStatus_200_Ok || status == HttpStatus_301_MovedPermanently || @@ -228,7 +302,7 @@ stateMachine_.ClearHeaders(); stateMachine_.SetHttpStatus(status); - stateMachine_.SendBody(NULL, 0); + stateMachine_.SendBody(message, messageSize); } @@ -249,25 +323,229 @@ stateMachine_.SendBody(NULL, 0); } - void HttpOutput::SendBody(const void* buffer, size_t length) + void HttpOutput::Answer(const void* buffer, + size_t length) { - stateMachine_.SendBody(buffer, length); - } + if (length == 0) + { + AnswerEmpty(); + return; + } + + HttpCompression compression = GetPreferredCompression(length); + + if (compression == HttpCompression_None) + { + stateMachine_.SetContentLength(length); + stateMachine_.SendBody(buffer, length); + return; + } + + std::string compressed, encoding; - void HttpOutput::SendBody(const std::string& str) - { - if (str.size() == 0) + switch (compression) { - stateMachine_.SendBody(NULL, 0); + case HttpCompression_Deflate: + { + encoding = "deflate"; + ZlibCompressor compressor; + // Do not prefix the buffer with its uncompressed size, to be compatible with "deflate" + compressor.SetPrefixWithUncompressedSize(false); + compressor.Compress(compressed, buffer, length); + break; + } + + case HttpCompression_Gzip: + { + encoding = "gzip"; + GzipCompressor compressor; + compressor.Compress(compressed, buffer, length); + break; + } + + default: + throw OrthancException(ErrorCode_InternalError); + } + + LOG(TRACE) << "Compressing a HTTP answer using " << encoding; + + // The body is empty, do not use HTTP compression + if (compressed.size() == 0) + { + AnswerEmpty(); } else { - stateMachine_.SendBody(str.c_str(), str.size()); + stateMachine_.AddHeader("Content-Encoding", encoding); + stateMachine_.SetContentLength(compressed.size()); + stateMachine_.SendBody(compressed.c_str(), compressed.size()); + } + + stateMachine_.CloseBody(); + } + + + void HttpOutput::Answer(const std::string& str) + { + Answer(str.size() == 0 ? NULL : str.c_str(), str.size()); + } + + + void HttpOutput::AnswerEmpty() + { + stateMachine_.CloseBody(); + } + + + void HttpOutput::StateMachine::StartMultipart(const std::string& subType, + const std::string& contentType) + { + if (subType != "mixed" && + subType != "related") + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + if (keepAlive_) + { + LOG(ERROR) << "Multipart answers are not implemented together with keep-alive connections"; + throw OrthancException(ErrorCode_NotImplemented); + } + + if (state_ != State_WritingHeader) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + if (status_ != HttpStatus_200_Ok) + { + SendBody(NULL, 0); + return; + } + + stream_.OnHttpStatusReceived(status_); + + std::string header = "HTTP/1.1 200 OK\r\n"; + + // Possibly add the cookies + for (std::list::const_iterator + it = headers_.begin(); it != headers_.end(); ++it) + { + if (!Toolbox::StartsWith(*it, "Set-Cookie: ")) + { + LOG(ERROR) << "The only headers that can be set in multipart answers are Set-Cookie (here: " << *it << " is set)"; + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + header += *it; + } + + multipartBoundary_ = Toolbox::GenerateUuid(); + multipartContentType_ = contentType; + header += "Content-Type: multipart/related; type=multipart/" + subType + "; boundary=" + multipartBoundary_ + "\r\n\r\n"; + + stream_.Send(true, header.c_str(), header.size()); + state_ = State_WritingMultipart; + } + + + void HttpOutput::StateMachine::SendMultipartItem(const void* item, size_t length) + { + std::string header = "--" + multipartBoundary_ + "\n"; + header += "Content-Type: " + multipartContentType_ + "\n"; + header += "Content-Length: " + boost::lexical_cast(length) + "\n"; + header += "MIME-Version: 1.0\n\n"; + + stream_.Send(false, header.c_str(), header.size()); + + if (length > 0) + { + stream_.Send(false, item, length); + } + + stream_.Send(false, "\n", 1); + } + + + void HttpOutput::StateMachine::CloseMultipart() + { + if (state_ != State_WritingMultipart) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + // The two lines below might throw an exception, if the client has + // closed the connection. Such an error is ignored. + try + { + std::string header = "--" + multipartBoundary_ + "--\n"; + stream_.Send(false, header.c_str(), header.size()); + } + catch (OrthancException&) + { + } + + state_ = State_Done; + } + + + void HttpOutput::SendMultipartItem(const std::string& item) + { + if (item.size() > 0) + { + stateMachine_.SendMultipartItem(item.c_str(), item.size()); + } + else + { + stateMachine_.SendMultipartItem(NULL, 0); } } - void HttpOutput::SendBody() + + void HttpOutput::Answer(IHttpStreamAnswer& stream) { - stateMachine_.SendBody(NULL, 0); + HttpCompression compression = stream.SetupHttpCompression(isGzipAllowed_, isDeflateAllowed_); + + switch (compression) + { + case HttpCompression_None: + break; + + case HttpCompression_Gzip: + stateMachine_.AddHeader("Content-Encoding", "gzip"); + break; + + case HttpCompression_Deflate: + stateMachine_.AddHeader("Content-Encoding", "deflate"); + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + stateMachine_.SetContentLength(stream.GetContentLength()); + + std::string contentType = stream.GetContentType(); + if (contentType.empty()) + { + contentType = "application/octet-stream"; + } + + stateMachine_.SetContentType(contentType.c_str()); + + std::string filename; + if (stream.HasContentFilename(filename)) + { + SetContentFilename(filename.c_str()); + } + + while (stream.ReadNextChunk()) + { + stateMachine_.SendBody(stream.GetChunkContent(), + stream.GetChunkSize()); + } + + stateMachine_.CloseBody(); } + } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/HttpOutput.h --- a/Core/HttpServer/HttpOutput.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/HttpOutput.h Wed Sep 30 13:23:31 2015 +0200 @@ -37,7 +37,8 @@ #include #include "../Enumerations.h" #include "IHttpOutputStream.h" -#include "HttpHandler.h" +#include "IHttpStreamAnswer.h" +#include "../Uuid.h" namespace Orthanc { @@ -48,14 +49,16 @@ class StateMachine : public boost::noncopyable { - private: + public: enum State { State_WritingHeader, State_WritingBody, + State_WritingMultipart, State_Done }; + private: IHttpOutputStream& stream_; State state_; @@ -66,6 +69,9 @@ bool keepAlive_; std::list headers_; + std::string multipartBoundary_; + std::string multipartContentType_; + public: StateMachine(IHttpOutputStream& stream, bool isKeepAlive); @@ -89,18 +95,71 @@ void ClearHeaders(); void SendBody(const void* buffer, size_t length); + + void StartMultipart(const std::string& subType, + const std::string& contentType); + + void SendMultipartItem(const void* item, size_t length); + + void CloseMultipart(); + + void CloseBody(); + + State GetState() const + { + return state_; + } }; StateMachine stateMachine_; + bool isDeflateAllowed_; + bool isGzipAllowed_; + + HttpCompression GetPreferredCompression(size_t bodySize) const; public: HttpOutput(IHttpOutputStream& stream, bool isKeepAlive) : - stateMachine_(stream, isKeepAlive) + stateMachine_(stream, isKeepAlive), + isDeflateAllowed_(false), + isGzipAllowed_(false) { } - void SendStatus(HttpStatus status); + void SetDeflateAllowed(bool allowed) + { + isDeflateAllowed_ = allowed; + } + + bool IsDeflateAllowed() const + { + return isDeflateAllowed_; + } + + void SetGzipAllowed(bool allowed) + { + isGzipAllowed_ = allowed; + } + + bool IsGzipAllowed() const + { + return isGzipAllowed_; + } + + void SendStatus(HttpStatus status, + const char* message, + size_t messageSize); + + void SendStatus(HttpStatus status) + { + SendStatus(status, NULL, 0); + } + + void SendStatus(HttpStatus status, + const std::string& message) + { + SendStatus(status, message.c_str(), message.size()); + } void SetContentType(const char* contentType) { @@ -112,11 +171,6 @@ stateMachine_.SetContentFilename(filename); } - void SetContentLength(uint64_t length) - { - stateMachine_.SetContentLength(length); - } - void SetCookie(const std::string& cookie, const std::string& value) { @@ -129,16 +183,42 @@ stateMachine_.AddHeader(key, value); } - void SendBody(const void* buffer, size_t length); + void Answer(const void* buffer, + size_t length); - void SendBody(const std::string& str); + void Answer(const std::string& str); - void SendBody(); + void AnswerEmpty(); void SendMethodNotAllowed(const std::string& allowed); void Redirect(const std::string& path); void SendUnauthorized(const std::string& realm); + + void StartMultipart(const std::string& subType, + const std::string& contentType) + { + stateMachine_.StartMultipart(subType, contentType); + } + + void SendMultipartItem(const std::string& item); + + void SendMultipartItem(const void* item, size_t size) + { + stateMachine_.SendMultipartItem(item, size); + } + + void CloseMultipart() + { + stateMachine_.CloseMultipart(); + } + + bool IsWritingMultipart() const + { + return stateMachine_.GetState() == StateMachine::State_WritingMultipart; + } + + void Answer(IHttpStreamAnswer& stream); }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/HttpStreamTranscoder.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/HttpStreamTranscoder.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,250 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "HttpStreamTranscoder.h" + +#include "../OrthancException.h" +#include "../Compression/ZlibCompressor.h" + +#include // For memcpy() +#include + +#include + +namespace Orthanc +{ + void HttpStreamTranscoder::ReadSource(std::string& buffer) + { + if (source_.SetupHttpCompression(false, false) != HttpCompression_None) + { + throw OrthancException(ErrorCode_InternalError); + } + + uint64_t size = source_.GetContentLength(); + if (static_cast(static_cast(size)) != size) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + buffer.resize(static_cast(size)); + size_t offset = 0; + + while (source_.ReadNextChunk()) + { + size_t chunkSize = static_cast(source_.GetChunkSize()); + memcpy(&buffer[offset], source_.GetChunkContent(), chunkSize); + offset += chunkSize; + } + + if (offset != size) + { + throw OrthancException(ErrorCode_InternalError); + } + } + + + HttpCompression HttpStreamTranscoder::SetupZlibCompression(bool deflateAllowed) + { + uint64_t size = source_.GetContentLength(); + + if (size == 0) + { + return HttpCompression_None; + } + + if (size < sizeof(uint64_t)) + { + throw OrthancException(ErrorCode_CorruptedFile); + } + + if (deflateAllowed) + { + bytesToSkip_ = sizeof(uint64_t); + + return HttpCompression_Deflate; + } + else + { + // TODO Use stream-based zlib decoding to reduce memory usage + std::string compressed; + ReadSource(compressed); + + uncompressed_.reset(new BufferHttpSender); + + ZlibCompressor compressor; + IBufferCompressor::Uncompress(uncompressed_->GetBuffer(), compressor, compressed); + + return HttpCompression_None; + } + } + + + HttpCompression HttpStreamTranscoder::SetupHttpCompression(bool gzipAllowed, + bool deflateAllowed) + { + if (ready_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + ready_ = true; + + switch (sourceCompression_) + { + case CompressionType_None: + return HttpCompression_None; + + case CompressionType_ZlibWithSize: + return SetupZlibCompression(deflateAllowed); + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + uint64_t HttpStreamTranscoder::GetContentLength() + { + if (!ready_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + if (uncompressed_.get() != NULL) + { + return uncompressed_->GetContentLength(); + } + else + { + uint64_t length = source_.GetContentLength(); + if (length < bytesToSkip_) + { + throw OrthancException(ErrorCode_InternalError); + } + + return length - bytesToSkip_; + } + } + + + bool HttpStreamTranscoder::ReadNextChunk() + { + if (!ready_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + if (uncompressed_.get() != NULL) + { + return uncompressed_->ReadNextChunk(); + } + + assert(skipped_ <= bytesToSkip_); + if (skipped_ == bytesToSkip_) + { + // We have already skipped the first bytes of the stream + currentChunkOffset_ = 0; + return source_.ReadNextChunk(); + } + + // This condition can only be true on the first call to "ReadNextChunk()" + for (;;) + { + assert(skipped_ < bytesToSkip_); + + bool ok = source_.ReadNextChunk(); + if (!ok) + { + throw OrthancException(ErrorCode_CorruptedFile); + } + + size_t remaining = static_cast(bytesToSkip_ - skipped_); + size_t s = source_.GetChunkSize(); + + if (s < remaining) + { + skipped_ += s; + } + else if (s == remaining) + { + // We have skipped enough bytes, but we must read a new chunk + currentChunkOffset_ = 0; + skipped_ = bytesToSkip_; + return source_.ReadNextChunk(); + } + else + { + // We have skipped enough bytes, and we have enough data in the current chunk + assert(s > remaining); + currentChunkOffset_ = remaining; + skipped_ = bytesToSkip_; + return true; + } + } + } + + + const char* HttpStreamTranscoder::GetChunkContent() + { + if (!ready_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + if (uncompressed_.get() != NULL) + { + return uncompressed_->GetChunkContent(); + } + else + { + return source_.GetChunkContent() + currentChunkOffset_; + } + } + + size_t HttpStreamTranscoder::GetChunkSize() + { + if (!ready_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + if (uncompressed_.get() != NULL) + { + return uncompressed_->GetChunkSize(); + } + else + { + return static_cast(source_.GetChunkSize() - currentChunkOffset_); + } + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/HttpStreamTranscoder.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/HttpStreamTranscoder.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,90 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "BufferHttpSender.h" + +#include // For std::auto_ptr + +namespace Orthanc +{ + class HttpStreamTranscoder : public IHttpStreamAnswer + { + private: + IHttpStreamAnswer& source_; + CompressionType sourceCompression_; + uint64_t bytesToSkip_; + uint64_t skipped_; + uint64_t currentChunkOffset_; + bool ready_; + + std::auto_ptr uncompressed_; + + void ReadSource(std::string& buffer); + + HttpCompression SetupZlibCompression(bool deflateAllowed); + + public: + HttpStreamTranscoder(IHttpStreamAnswer& source, + CompressionType compression) : + source_(source), + sourceCompression_(compression), + bytesToSkip_(0), + skipped_(0), + ready_(false) + { + } + + // This is the first method to be called + virtual HttpCompression SetupHttpCompression(bool gzipAllowed, + bool deflateAllowed); + + virtual bool HasContentFilename(std::string& filename) + { + return source_.HasContentFilename(filename); + } + + virtual std::string GetContentType() + { + return source_.GetContentType(); + } + + virtual uint64_t GetContentLength(); + + virtual bool ReadNextChunk(); + + virtual const char* GetChunkContent(); + + virtual size_t GetChunkSize(); + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/HttpToolbox.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/HttpToolbox.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,296 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "HttpToolbox.h" + +#include +#include +#include + +#include "HttpOutput.h" +#include "StringHttpOutput.h" + + +static const char* LOCALHOST = "localhost"; + + + +namespace Orthanc +{ + static void SplitGETNameValue(IHttpHandler::GetArguments& result, + const char* start, + const char* end) + { + std::string name, value; + + const char* equal = strchr(start, '='); + if (equal == NULL || equal >= end) + { + name = std::string(start, end - start); + //value = ""; + } + else + { + name = std::string(start, equal - start); + value = std::string(equal + 1, end); + } + + Toolbox::UrlDecode(name); + Toolbox::UrlDecode(value); + + result.push_back(std::make_pair(name, value)); + } + + + void HttpToolbox::ParseGetArguments(IHttpHandler::GetArguments& result, + const char* query) + { + const char* pos = query; + + while (pos != NULL) + { + const char* ampersand = strchr(pos, '&'); + if (ampersand) + { + SplitGETNameValue(result, pos, ampersand); + pos = ampersand + 1; + } + else + { + // No more ampersand, this is the last argument + SplitGETNameValue(result, pos, pos + strlen(pos)); + pos = NULL; + } + } + } + + + void HttpToolbox::ParseGetQuery(UriComponents& uri, + IHttpHandler::GetArguments& getArguments, + const char* query) + { + const char *questionMark = ::strchr(query, '?'); + if (questionMark == NULL) + { + // No question mark in the string + Toolbox::SplitUriComponents(uri, query); + getArguments.clear(); + } + else + { + Toolbox::SplitUriComponents(uri, std::string(query, questionMark)); + HttpToolbox::ParseGetArguments(getArguments, questionMark + 1); + } + } + + + std::string HttpToolbox::GetArgument(const IHttpHandler::Arguments& getArguments, + const std::string& name, + const std::string& defaultValue) + { + IHttpHandler::Arguments::const_iterator it = getArguments.find(name); + if (it == getArguments.end()) + { + return defaultValue; + } + else + { + return it->second; + } + } + + + std::string HttpToolbox::GetArgument(const IHttpHandler::GetArguments& getArguments, + const std::string& name, + const std::string& defaultValue) + { + for (size_t i = 0; i < getArguments.size(); i++) + { + if (getArguments[i].first == name) + { + return getArguments[i].second; + } + } + + return defaultValue; + } + + + + void HttpToolbox::ParseCookies(IHttpHandler::Arguments& result, + const IHttpHandler::Arguments& httpHeaders) + { + result.clear(); + + IHttpHandler::Arguments::const_iterator it = httpHeaders.find("cookie"); + if (it != httpHeaders.end()) + { + const std::string& cookies = it->second; + + size_t pos = 0; + while (pos != std::string::npos) + { + size_t nextSemicolon = cookies.find(";", pos); + std::string cookie; + + if (nextSemicolon == std::string::npos) + { + cookie = cookies.substr(pos); + pos = std::string::npos; + } + else + { + cookie = cookies.substr(pos, nextSemicolon - pos); + pos = nextSemicolon + 1; + } + + size_t equal = cookie.find("="); + if (equal != std::string::npos) + { + std::string name = Toolbox::StripSpaces(cookie.substr(0, equal)); + std::string value = Toolbox::StripSpaces(cookie.substr(equal + 1)); + result[name] = value; + } + } + } + } + + + void HttpToolbox::CompileGetArguments(IHttpHandler::Arguments& compiled, + const IHttpHandler::GetArguments& source) + { + compiled.clear(); + + for (size_t i = 0; i < source.size(); i++) + { + compiled[source[i].first] = source[i].second; + } + } + + + bool HttpToolbox::SimpleGet(std::string& result, + IHttpHandler& handler, + RequestOrigin origin, + const std::string& uri) + { + IHttpHandler::Arguments headers; // No HTTP header + + UriComponents curi; + IHttpHandler::GetArguments getArguments; + ParseGetQuery(curi, getArguments, uri.c_str()); + + StringHttpOutput stream; + HttpOutput http(stream, false /* no keep alive */); + + if (handler.Handle(http, origin, LOCALHOST, "", HttpMethod_Get, curi, + headers, getArguments, NULL /* no body for GET */, 0)) + { + stream.GetOutput(result); + return true; + } + else + { + return false; + } + } + + + static bool SimplePostOrPut(std::string& result, + IHttpHandler& handler, + RequestOrigin origin, + HttpMethod method, + const std::string& uri, + const char* bodyData, + size_t bodySize) + { + IHttpHandler::Arguments headers; // No HTTP header + IHttpHandler::GetArguments getArguments; // No GET argument for POST/PUT + + UriComponents curi; + Toolbox::SplitUriComponents(curi, uri); + + StringHttpOutput stream; + HttpOutput http(stream, false /* no keep alive */); + + if (handler.Handle(http, origin, LOCALHOST, "", method, curi, + headers, getArguments, bodyData, bodySize)) + { + stream.GetOutput(result); + return true; + } + else + { + return false; + } + } + + + bool HttpToolbox::SimplePost(std::string& result, + IHttpHandler& handler, + RequestOrigin origin, + const std::string& uri, + const char* bodyData, + size_t bodySize) + { + return SimplePostOrPut(result, handler, origin, HttpMethod_Post, uri, bodyData, bodySize); + } + + + bool HttpToolbox::SimplePut(std::string& result, + IHttpHandler& handler, + RequestOrigin origin, + const std::string& uri, + const char* bodyData, + size_t bodySize) + { + return SimplePostOrPut(result, handler, origin, HttpMethod_Put, uri, bodyData, bodySize); + } + + + bool HttpToolbox::SimpleDelete(IHttpHandler& handler, + RequestOrigin origin, + const std::string& uri) + { + UriComponents curi; + Toolbox::SplitUriComponents(curi, uri); + + IHttpHandler::Arguments headers; // No HTTP header + IHttpHandler::GetArguments getArguments; // No GET argument for DELETE + + StringHttpOutput stream; + HttpOutput http(stream, false /* no keep alive */); + + return handler.Handle(http, origin, LOCALHOST, "", HttpMethod_Delete, curi, + headers, getArguments, NULL /* no body for DELETE */, 0); + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/HttpToolbox.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/HttpToolbox.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,86 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IHttpHandler.h" + +namespace Orthanc +{ + class HttpToolbox + { + public: + static void ParseGetArguments(IHttpHandler::GetArguments& result, + const char* query); + + static void ParseGetQuery(UriComponents& uri, + IHttpHandler::GetArguments& getArguments, + const char* query); + + static std::string GetArgument(const IHttpHandler::Arguments& getArguments, + const std::string& name, + const std::string& defaultValue); + + static std::string GetArgument(const IHttpHandler::GetArguments& getArguments, + const std::string& name, + const std::string& defaultValue); + + static void ParseCookies(IHttpHandler::Arguments& result, + const IHttpHandler::Arguments& httpHeaders); + + static void CompileGetArguments(IHttpHandler::Arguments& compiled, + const IHttpHandler::GetArguments& source); + + static bool SimpleGet(std::string& result, + IHttpHandler& handler, + RequestOrigin origin, + const std::string& uri); + + static bool SimplePost(std::string& result, + IHttpHandler& handler, + RequestOrigin origin, + const std::string& uri, + const char* bodyData, + size_t bodySize); + + static bool SimplePut(std::string& result, + IHttpHandler& handler, + RequestOrigin origin, + const std::string& uri, + const char* bodyData, + size_t bodySize); + + static bool SimpleDelete(IHttpHandler& handler, + RequestOrigin origin, + const std::string& uri); + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/IHttpHandler.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/IHttpHandler.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,66 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Enumerations.h" +#include "HttpOutput.h" + +#include +#include +#include +#include + +namespace Orthanc +{ + class IHttpHandler : public boost::noncopyable + { + public: + typedef std::map Arguments; + typedef std::vector< std::pair > GetArguments; + + virtual ~IHttpHandler() + { + } + + virtual bool Handle(HttpOutput& output, + RequestOrigin origin, + const char* remoteIp, + const char* username, + HttpMethod method, + const UriComponents& uri, + const Arguments& headers, + const GetArguments& getArguments, + const char* bodyData, + size_t bodySize) = 0; + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/IHttpStreamAnswer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/IHttpStreamAnswer.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,66 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Enumerations.h" + +#include +#include +#include + +namespace Orthanc +{ + class IHttpStreamAnswer : public boost::noncopyable + { + public: + virtual ~IHttpStreamAnswer() + { + } + + // This is the first method to be called + virtual HttpCompression SetupHttpCompression(bool gzipAllowed, + bool deflateAllowed) = 0; + + virtual bool HasContentFilename(std::string& filename) = 0; + + virtual std::string GetContentType() = 0; + + virtual uint64_t GetContentLength() = 0; + + virtual bool ReadNextChunk() = 0; + + virtual const char* GetChunkContent() = 0; + + virtual size_t GetChunkSize() = 0; + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/MongooseServer.cpp --- a/Core/HttpServer/MongooseServer.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/MongooseServer.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -35,6 +35,11 @@ #include "../PrecompiledHeaders.h" #include "MongooseServer.h" +#include "../Logging.h" +#include "../ChunkedBuffer.h" +#include "HttpToolbox.h" +#include "mongoose.h" + #include #include #include @@ -43,12 +48,6 @@ #include #include #include -#include - -#include "../OrthancException.h" -#include "../ChunkedBuffer.h" -#include "HttpOutput.h" -#include "mongoose.h" #if ORTHANC_SSL_ENABLED == 1 #include @@ -82,7 +81,12 @@ { if (length > 0) { - mg_write(connection_, buffer, length); + int status = mg_write(connection_, buffer, length); + if (status != static_cast(length)) + { + // status == 0 when the connection has been closed, -1 on error + throw OrthancException(ErrorCode_NetworkProtocol); + } } } @@ -262,9 +266,9 @@ static PostDataStatus ReadBody(std::string& postData, struct mg_connection *connection, - const HttpHandler::Arguments& headers) + const IHttpHandler::Arguments& headers) { - HttpHandler::Arguments::const_iterator cs = headers.find("content-length"); + IHttpHandler::Arguments::const_iterator cs = headers.find("content-length"); if (cs == headers.end()) { return PostDataStatus_NoLength; @@ -308,7 +312,7 @@ static PostDataStatus ParseMultipartPost(std::string &completedFile, struct mg_connection *connection, - const HttpHandler::Arguments& headers, + const IHttpHandler::Arguments& headers, const std::string& contentType, ChunkStore& chunkStore) { @@ -322,13 +326,13 @@ return status; } - /*for (HttpHandler::Arguments::const_iterator i = headers.begin(); i != headers.end(); i++) + /*for (IHttpHandler::Arguments::const_iterator i = headers.begin(); i != headers.end(); i++) { std::cout << "Header [" << i->first << "] = " << i->second << "\n"; } printf("CHUNK\n");*/ - typedef HttpHandler::Arguments::const_iterator ArgumentIterator; + typedef IHttpHandler::Arguments::const_iterator ArgumentIterator; ArgumentIterator requestedWith = headers.find("x-requested-with"); ArgumentIterator fileName = headers.find("x-file-name"); @@ -410,11 +414,11 @@ static bool IsAccessGranted(const MongooseServer& that, - const HttpHandler::Arguments& headers) + const IHttpHandler::Arguments& headers) { bool granted = false; - HttpHandler::Arguments::const_iterator auth = headers.find("authorization"); + IHttpHandler::Arguments::const_iterator auth = headers.find("authorization"); if (auth != headers.end()) { std::string s = auth->second; @@ -430,9 +434,9 @@ } - static std::string GetAuthenticatedUsername(const HttpHandler::Arguments& headers) + static std::string GetAuthenticatedUsername(const IHttpHandler::Arguments& headers) { - HttpHandler::Arguments::const_iterator auth = headers.find("authorization"); + IHttpHandler::Arguments::const_iterator auth = headers.find("authorization"); if (auth == headers.end()) { @@ -465,15 +469,15 @@ static bool ExtractMethod(HttpMethod& method, const struct mg_request_info *request, - const HttpHandler::Arguments& headers, - const HttpHandler::Arguments& argumentsGET) + const IHttpHandler::Arguments& headers, + const IHttpHandler::GetArguments& argumentsGET) { std::string overriden; // Check whether some PUT/DELETE faking is done // 1. Faking with Google's approach - HttpHandler::Arguments::const_iterator methodOverride = + IHttpHandler::Arguments::const_iterator methodOverride = headers.find("x-http-method-override"); if (methodOverride != headers.end()) @@ -484,10 +488,13 @@ { // 2. Faking with Ruby on Rail's approach // GET /my/resource?_method=delete <=> DELETE /my/resource - methodOverride = argumentsGET.find("_method"); - if (methodOverride != argumentsGET.end()) + for (size_t i = 0; i < argumentsGET.size(); i++) { - overriden = methodOverride->second; + if (argumentsGET[i].first == "_method") + { + overriden = argumentsGET[i].second; + break; + } } } @@ -540,10 +547,39 @@ } + static void ConfigureHttpCompression(HttpOutput& output, + const IHttpHandler::Arguments& headers) + { + // Look if the client wishes HTTP compression + // https://en.wikipedia.org/wiki/HTTP_compression + IHttpHandler::Arguments::const_iterator it = headers.find("accept-encoding"); + if (it != headers.end()) + { + std::vector encodings; + Toolbox::TokenizeString(encodings, it->second, ','); + + for (size_t i = 0; i < encodings.size(); i++) + { + std::string s = Toolbox::StripSpaces(encodings[i]); + + if (s == "deflate") + { + output.SetDeflateAllowed(true); + } + else if (s == "gzip") + { + output.SetGzipAllowed(true); + } + } + } + } + + static void InternalCallback(struct mg_connection *connection, const struct mg_request_info *request) { MongooseServer* that = reinterpret_cast(request->user_data); + MongooseOutputStream stream(connection); HttpOutput output(stream, that->IsKeepAliveEnabled()); @@ -557,7 +593,7 @@ // Extract the HTTP headers - HttpHandler::Arguments headers; + IHttpHandler::Arguments headers; for (int i = 0; i < request->num_headers; i++) { std::string name = request->http_headers[i].name; @@ -565,12 +601,17 @@ headers.insert(std::make_pair(name, request->http_headers[i].value)); } + if (that->IsHttpCompressionEnabled()) + { + ConfigureHttpCompression(output, headers); + } + // Extract the GET arguments - HttpHandler::Arguments argumentsGET; + IHttpHandler::GetArguments argumentsGET; if (!strcmp(request->request_method, "GET")) { - HttpHandler::ParseGetArguments(argumentsGET, request->query_string); + HttpToolbox::ParseGetArguments(argumentsGET, request->query_string); } @@ -592,18 +633,18 @@ // Apply the filter, if it is installed + char remoteIp[24]; + sprintf(remoteIp, "%d.%d.%d.%d", + reinterpret_cast(&request->remote_ip) [3], + reinterpret_cast(&request->remote_ip) [2], + reinterpret_cast(&request->remote_ip) [1], + reinterpret_cast(&request->remote_ip) [0]); + + std::string username = GetAuthenticatedUsername(headers); + const IIncomingHttpRequestFilter *filter = that->GetIncomingHttpRequestFilter(); if (filter != NULL) { - std::string username = GetAuthenticatedUsername(headers); - - char remoteIp[24]; - sprintf(remoteIp, "%d.%d.%d.%d", - reinterpret_cast(&request->remote_ip) [3], - reinterpret_cast(&request->remote_ip) [2], - reinterpret_cast(&request->remote_ip) [1], - reinterpret_cast(&request->remote_ip) [0]); - if (!filter->IsAllowed(method, request->uri, remoteIp, username.c_str())) { output.SendUnauthorized(ORTHANC_REALM); @@ -613,13 +654,16 @@ // Extract the body of the request for PUT and POST + + // TODO Avoid unneccessary memcopy of the body + std::string body; if (method == HttpMethod_Post || method == HttpMethod_Put) { PostDataStatus status; - HttpHandler::Arguments::const_iterator ct = headers.find("content-type"); + IHttpHandler::Arguments::const_iterator ct = headers.find("content-type"); if (ct == headers.end()) { // No content-type specified. Assume no multi-part content occurs at this point. @@ -650,7 +694,7 @@ return; case PostDataStatus_Pending: - output.SendBody(); + output.AnswerEmpty(); return; default: @@ -672,58 +716,58 @@ } - // Loop over the candidate handlers for this URI LOG(INFO) << EnumerationToString(method) << " " << Toolbox::FlattenUri(uri); - bool found = false; + - for (MongooseServer::Handlers::const_iterator it = - that->GetHandlers().begin(); it != that->GetHandlers().end() && !found; ++it) + try { + bool found = false; + try { - found = (*it)->Handle(output, method, uri, headers, argumentsGET, body); - } - catch (OrthancException& e) - { - // Using this candidate handler results in an exception - LOG(ERROR) << "Exception in the HTTP handler: " << e.What(); - - switch (e.GetErrorCode()) + if (that->HasHandler()) { - case ErrorCode_InexistentFile: - case ErrorCode_InexistentItem: - case ErrorCode_UnknownResource: - output.SendStatus(HttpStatus_404_NotFound); - break; - - case ErrorCode_BadRequest: - case ErrorCode_UriSyntax: - output.SendStatus(HttpStatus_400_BadRequest); - break; - - default: - output.SendStatus(HttpStatus_500_InternalServerError); + found = that->GetHandler().Handle(output, RequestOrigin_Http, remoteIp, username.c_str(), + method, uri, headers, argumentsGET, body.c_str(), body.size()); } - - return; } catch (boost::bad_lexical_cast&) { - LOG(ERROR) << "Exception in the HTTP handler: Bad lexical cast"; - output.SendStatus(HttpStatus_400_BadRequest); - return; + throw OrthancException(ErrorCode_BadParameterType); } catch (std::runtime_error&) { - LOG(ERROR) << "Exception in the HTTP handler: Presumably a bad JSON request"; - output.SendStatus(HttpStatus_400_BadRequest); - return; + // Presumably an error while parsing the JSON body + throw OrthancException(ErrorCode_BadRequest); + } + + if (!found) + { + throw OrthancException(ErrorCode_UnknownResource); } } + catch (OrthancException& e) + { + // Using this candidate handler results in an exception + try + { + if (that->GetExceptionFormatter() == NULL) + { + LOG(ERROR) << "Exception in the HTTP handler: " << e.What(); + output.SendStatus(e.GetHttpStatus()); + } + else + { + that->GetExceptionFormatter()->Format(output, e, method, request->uri); + } + } + catch (OrthancException&) + { + // An exception here reflects the fact that the status code + // was already set by the HTTP handler. + } - if (!found) - { - output.SendStatus(HttpStatus_404_NotFound); + return; } } @@ -773,12 +817,15 @@ MongooseServer::MongooseServer() : pimpl_(new PImpl) { pimpl_->context_ = NULL; + handler_ = NULL; remoteAllowed_ = false; authentication_ = false; ssl_ = false; port_ = 8000; filter_ = NULL; keepAlive_ = false; + httpCompression_ = true; + exceptionFormatter_ = NULL; #if ORTHANC_SSL_ENABLED == 1 // Check for the Heartbleed exploit @@ -795,7 +842,6 @@ MongooseServer::~MongooseServer() { Stop(); - ClearHandlers(); } @@ -845,7 +891,7 @@ if (!pimpl_->context_) { - throw OrthancException("Unable to launch the Mongoose server"); + throw OrthancException(ErrorCode_HttpPortInUse); } } } @@ -860,20 +906,6 @@ } - void MongooseServer::RegisterHandler(HttpHandler& handler) - { - Stop(); - - handlers_.push_back(&handler); - } - - - void MongooseServer::ClearHandlers() - { - Stop(); - } - - void MongooseServer::ClearUsers() { Stop(); @@ -937,14 +969,47 @@ remoteAllowed_ = allowed; } + void MongooseServer::SetHttpCompressionEnabled(bool enabled) + { + Stop(); + httpCompression_ = enabled; + LOG(WARNING) << "HTTP compression is " << (enabled ? "enabled" : "disabled"); + } + void MongooseServer::SetIncomingHttpRequestFilter(IIncomingHttpRequestFilter& filter) { Stop(); filter_ = &filter; } + + void MongooseServer::SetHttpExceptionFormatter(IHttpExceptionFormatter& formatter) + { + Stop(); + exceptionFormatter_ = &formatter; + } + + bool MongooseServer::IsValidBasicHttpAuthentication(const std::string& basic) const { return registeredUsers_.find(basic) != registeredUsers_.end(); } + + + void MongooseServer::Register(IHttpHandler& handler) + { + Stop(); + handler_ = &handler; + } + + + IHttpHandler& MongooseServer::GetHandler() const + { + if (handler_ == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + + return *handler_; + } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/MongooseServer.h --- a/Core/HttpServer/MongooseServer.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/HttpServer/MongooseServer.h Wed Sep 30 13:23:31 2015 +0200 @@ -32,7 +32,9 @@ #pragma once -#include "HttpHandler.h" +#include "IHttpHandler.h" + +#include "../OrthancException.h" #include #include @@ -57,17 +59,29 @@ const char* username) const = 0; }; - class MongooseServer + + class IHttpExceptionFormatter { public: - typedef std::list Handlers; + virtual ~IHttpExceptionFormatter() + { + } + virtual void Format(HttpOutput& output, + const OrthancException& exception, + HttpMethod method, + const char* uri) = 0; + }; + + + class MongooseServer + { private: // http://stackoverflow.com/questions/311166/stdauto-ptr-or-boostshared-ptr-for-pimpl-idiom struct PImpl; boost::shared_ptr pimpl_; - Handlers handlers_; + IHttpHandler *handler_; typedef std::set RegisteredUsers; RegisteredUsers registeredUsers_; @@ -79,6 +93,8 @@ uint16_t port_; IIncomingHttpRequestFilter* filter_; bool keepAlive_; + bool httpCompression_; + IHttpExceptionFormatter* exceptionFormatter_; bool IsRunning() const; @@ -103,8 +119,6 @@ void RegisterUser(const char* username, const char* password); - void RegisterHandler(HttpHandler& handler); - bool IsAuthenticationEnabled() const { return authentication_; @@ -140,6 +154,13 @@ void SetRemoteAccessAllowed(bool allowed); + bool IsHttpCompressionEnabled() const + { + return httpCompression_;; + } + + void SetHttpCompressionEnabled(bool enabled); + const IIncomingHttpRequestFilter* GetIncomingHttpRequestFilter() const { return filter_; @@ -147,15 +168,24 @@ void SetIncomingHttpRequestFilter(IIncomingHttpRequestFilter& filter); - void ClearHandlers(); - ChunkStore& GetChunkStore(); bool IsValidBasicHttpAuthentication(const std::string& basic) const; - const Handlers& GetHandlers() const + void Register(IHttpHandler& handler); + + bool HasHandler() const { - return handlers_; + return handler_ != NULL; + } + + IHttpHandler& GetHandler() const; + + void SetHttpExceptionFormatter(IHttpExceptionFormatter& formatter); + + IHttpExceptionFormatter* GetExceptionFormatter() + { + return exceptionFormatter_; } }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/StringHttpOutput.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/StringHttpOutput.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,55 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "StringHttpOutput.h" + +#include "../OrthancException.h" + +namespace Orthanc +{ + void StringHttpOutput::OnHttpStatusReceived(HttpStatus status) + { + if (status != HttpStatus_200_Ok) + { + throw OrthancException(ErrorCode_BadRequest); + } + } + + void StringHttpOutput::Send(bool isHeader, const void* buffer, size_t length) + { + if (!isHeader) + { + buffer_.AddChunk(reinterpret_cast(buffer), length); + } + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/HttpServer/StringHttpOutput.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/StringHttpOutput.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,56 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IHttpOutputStream.h" + +#include "../ChunkedBuffer.h" + +namespace Orthanc +{ + class StringHttpOutput : public IHttpOutputStream + { + private: + ChunkedBuffer buffer_; + + public: + virtual void OnHttpStatusReceived(HttpStatus status); + + virtual void Send(bool isHeader, const void* buffer, size_t length); + + void GetOutput(std::string& output) + { + buffer_.Flatten(output); + } + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/ImageFormats/ImageAccessor.cpp --- a/Core/ImageFormats/ImageAccessor.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,221 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeaders.h" -#include "ImageAccessor.h" - -#include "../OrthancException.h" -#include "../ChunkedBuffer.h" - -#include -#include -#include -#include - -namespace Orthanc -{ - template - static void ToMatlabStringInternal(ChunkedBuffer& target, - const ImageAccessor& source) - { - target.AddChunk("double([ "); - - for (unsigned int y = 0; y < source.GetHeight(); y++) - { - const PixelType* p = reinterpret_cast(source.GetConstRow(y)); - - std::string s; - if (y > 0) - { - s = "; "; - } - - s.reserve(source.GetWidth() * 8); - - for (unsigned int x = 0; x < source.GetWidth(); x++, p++) - { - s += boost::lexical_cast(static_cast(*p)) + " "; - } - - target.AddChunk(s); - } - - target.AddChunk("])"); - } - - - static void RGB24ToMatlabString(ChunkedBuffer& target, - const ImageAccessor& source) - { - assert(source.GetFormat() == PixelFormat_RGB24); - - target.AddChunk("double(permute(reshape([ "); - - for (unsigned int y = 0; y < source.GetHeight(); y++) - { - const uint8_t* p = reinterpret_cast(source.GetConstRow(y)); - - std::string s; - s.reserve(source.GetWidth() * 3 * 8); - - for (unsigned int x = 0; x < 3 * source.GetWidth(); x++, p++) - { - s += boost::lexical_cast(static_cast(*p)) + " "; - } - - target.AddChunk(s); - } - - target.AddChunk("], [ 3 " + boost::lexical_cast(source.GetHeight()) + - " " + boost::lexical_cast(source.GetWidth()) + " ]), [ 3 2 1 ]))"); - } - - - void* ImageAccessor::GetBuffer() const - { - if (readOnly_) - { - LOG(ERROR) << "Trying to write on a read-only image"; - throw OrthancException(ErrorCode_ReadOnly); - } - - return buffer_; - } - - - const void* ImageAccessor::GetConstRow(unsigned int y) const - { - if (buffer_ != NULL) - { - return reinterpret_cast(buffer_) + y * pitch_; - } - else - { - return NULL; - } - } - - - void* ImageAccessor::GetRow(unsigned int y) const - { - if (readOnly_) - { - LOG(ERROR) << "Trying to write on a read-only image"; - throw OrthancException(ErrorCode_ReadOnly); - } - - if (buffer_ != NULL) - { - return reinterpret_cast(buffer_) + y * pitch_; - } - else - { - return NULL; - } - } - - - void ImageAccessor::AssignEmpty(PixelFormat format) - { - readOnly_ = false; - format_ = format; - width_ = 0; - height_ = 0; - pitch_ = 0; - buffer_ = NULL; - } - - - void ImageAccessor::AssignReadOnly(PixelFormat format, - unsigned int width, - unsigned int height, - unsigned int pitch, - const void *buffer) - { - readOnly_ = true; - format_ = format; - width_ = width; - height_ = height; - pitch_ = pitch; - buffer_ = const_cast(buffer); - - assert(GetBytesPerPixel() * width_ <= pitch_); - } - - - void ImageAccessor::AssignWritable(PixelFormat format, - unsigned int width, - unsigned int height, - unsigned int pitch, - void *buffer) - { - readOnly_ = false; - format_ = format; - width_ = width; - height_ = height; - pitch_ = pitch; - buffer_ = buffer; - - assert(GetBytesPerPixel() * width_ <= pitch_); - } - - - void ImageAccessor::ToMatlabString(std::string& target) const - { - ChunkedBuffer buffer; - - switch (GetFormat()) - { - case PixelFormat_Grayscale8: - ToMatlabStringInternal(buffer, *this); - break; - - case PixelFormat_Grayscale16: - ToMatlabStringInternal(buffer, *this); - break; - - case PixelFormat_SignedGrayscale16: - ToMatlabStringInternal(buffer, *this); - break; - - case PixelFormat_RGB24: - RGB24ToMatlabString(buffer, *this); - break; - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - - buffer.Flatten(target); - } - -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/ImageFormats/ImageAccessor.h --- a/Core/ImageFormats/ImageAccessor.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../Enumerations.h" - -namespace Orthanc -{ - class ImageAccessor - { - private: - bool readOnly_; - PixelFormat format_; - unsigned int width_; - unsigned int height_; - unsigned int pitch_; - void *buffer_; - - public: - ImageAccessor() - { - AssignEmpty(PixelFormat_Grayscale8); - } - - bool IsReadOnly() const - { - return readOnly_; - } - - PixelFormat GetFormat() const - { - return format_; - } - - unsigned int GetBytesPerPixel() const - { - return ::Orthanc::GetBytesPerPixel(format_); - } - - unsigned int GetWidth() const - { - return width_; - } - - unsigned int GetHeight() const - { - return height_; - } - - unsigned int GetPitch() const - { - return pitch_; - } - - unsigned int GetSize() const - { - return GetHeight() * GetPitch(); - } - - const void* GetConstBuffer() const - { - return buffer_; - } - - void* GetBuffer() const; - - const void* GetConstRow(unsigned int y) const; - - void* GetRow(unsigned int y) const; - - void AssignEmpty(PixelFormat format); - - void AssignReadOnly(PixelFormat format, - unsigned int width, - unsigned int height, - unsigned int pitch, - const void *buffer); - - void AssignWritable(PixelFormat format, - unsigned int width, - unsigned int height, - unsigned int pitch, - void *buffer); - - void ToMatlabString(std::string& target) const; - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/ImageFormats/ImageBuffer.cpp --- a/Core/ImageFormats/ImageBuffer.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,192 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeaders.h" -#include "ImageBuffer.h" - -#include "../OrthancException.h" - -#include -#include - -namespace Orthanc -{ - void ImageBuffer::Allocate() - { - if (changed_) - { - Deallocate(); - - /* - if (forceMinimalPitch_) - { - TODO: Align pitch and memory buffer to optimal size for SIMD. - } - */ - - pitch_ = GetBytesPerPixel() * width_; - size_t size = pitch_ * height_; - - if (size == 0) - { - buffer_ = NULL; - } - else - { - buffer_ = malloc(size); - if (buffer_ == NULL) - { - throw OrthancException(ErrorCode_NotEnoughMemory); - } - } - - changed_ = false; - } - } - - - void ImageBuffer::Deallocate() - { - if (buffer_ != NULL) - { - free(buffer_); - buffer_ = NULL; - changed_ = true; - } - } - - - ImageBuffer::ImageBuffer(unsigned int width, - unsigned int height, - PixelFormat format) - { - Initialize(); - SetWidth(width); - SetHeight(height); - SetFormat(format); - } - - - void ImageBuffer::Initialize() - { - changed_ = false; - forceMinimalPitch_ = true; - format_ = PixelFormat_Grayscale8; - width_ = 0; - height_ = 0; - pitch_ = 0; - buffer_ = NULL; - } - - - void ImageBuffer::SetFormat(PixelFormat format) - { - if (format != format_) - { - changed_ = true; - format_ = format; - } - } - - - void ImageBuffer::SetWidth(unsigned int width) - { - if (width != width_) - { - changed_ = true; - width_ = width; - } - } - - - void ImageBuffer::SetHeight(unsigned int height) - { - if (height != height_) - { - changed_ = true; - height_ = height; - } - } - - - ImageAccessor ImageBuffer::GetAccessor() - { - Allocate(); - - ImageAccessor accessor; - accessor.AssignWritable(format_, width_, height_, pitch_, buffer_); - return accessor; - } - - - ImageAccessor ImageBuffer::GetConstAccessor() - { - Allocate(); - - ImageAccessor accessor; - accessor.AssignReadOnly(format_, width_, height_, pitch_, buffer_); - return accessor; - } - - - void ImageBuffer::SetMinimalPitchForced(bool force) - { - if (force != forceMinimalPitch_) - { - changed_ = true; - forceMinimalPitch_ = force; - } - } - - - void ImageBuffer::AcquireOwnership(ImageBuffer& other) - { - // Remove the content of the current image - Deallocate(); - - // Force the allocation of the other image (if not already - // allocated) - other.Allocate(); - - // Transfer the content of the other image - changed_ = false; - forceMinimalPitch_ = other.forceMinimalPitch_; - format_ = other.format_; - width_ = other.width_; - height_ = other.height_; - pitch_ = other.pitch_; - buffer_ = other.buffer_; - - // Force the reinitialization of the other image - other.Initialize(); - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/ImageFormats/ImageBuffer.h --- a/Core/ImageFormats/ImageBuffer.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ImageAccessor.h" - -#include -#include -#include - -namespace Orthanc -{ - class ImageBuffer : public boost::noncopyable - { - private: - bool changed_; - - bool forceMinimalPitch_; // Currently unused - PixelFormat format_; - unsigned int width_; - unsigned int height_; - unsigned int pitch_; - void *buffer_; - - void Initialize(); - - void Allocate(); - - void Deallocate(); - - public: - ImageBuffer(unsigned int width, - unsigned int height, - PixelFormat format); - - ImageBuffer() - { - Initialize(); - } - - ~ImageBuffer() - { - Deallocate(); - } - - PixelFormat GetFormat() const - { - return format_; - } - - void SetFormat(PixelFormat format); - - unsigned int GetWidth() const - { - return width_; - } - - void SetWidth(unsigned int width); - - unsigned int GetHeight() const - { - return height_; - } - - void SetHeight(unsigned int height); - - unsigned int GetBytesPerPixel() const - { - return ::Orthanc::GetBytesPerPixel(format_); - } - - ImageAccessor GetAccessor(); - - ImageAccessor GetConstAccessor(); - - bool IsMinimalPitchForced() const - { - return forceMinimalPitch_; - } - - void SetMinimalPitchForced(bool force); - - void AcquireOwnership(ImageBuffer& other); - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/ImageFormats/ImageProcessing.cpp --- a/Core/ImageFormats/ImageProcessing.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,532 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeaders.h" -#include "ImageProcessing.h" - -#include "../OrthancException.h" - -#include - -#include -#include -#include -#include - -namespace Orthanc -{ - template - static void ConvertInternal(ImageAccessor& target, - const ImageAccessor& source) - { - const TargetType minValue = std::numeric_limits::min(); - const TargetType maxValue = std::numeric_limits::max(); - - for (unsigned int y = 0; y < source.GetHeight(); y++) - { - TargetType* t = reinterpret_cast(target.GetRow(y)); - const SourceType* s = reinterpret_cast(source.GetConstRow(y)); - - for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s++) - { - if (static_cast(*s) < static_cast(minValue)) - { - *t = minValue; - } - else if (static_cast(*s) > static_cast(maxValue)) - { - *t = maxValue; - } - else - { - *t = static_cast(*s); - } - } - } - } - - - template - static void ConvertColorToGrayscale(ImageAccessor& target, - const ImageAccessor& source) - { - assert(source.GetFormat() == PixelFormat_RGB24); - - const TargetType minValue = std::numeric_limits::min(); - const TargetType maxValue = std::numeric_limits::max(); - - for (unsigned int y = 0; y < source.GetHeight(); y++) - { - TargetType* t = reinterpret_cast(target.GetRow(y)); - const uint8_t* s = reinterpret_cast(source.GetConstRow(y)); - - for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s += 3) - { - // Y = 0.2126 R + 0.7152 G + 0.0722 B - int32_t v = (2126 * static_cast(s[0]) + - 7152 * static_cast(s[1]) + - 0722 * static_cast(s[2])) / 1000; - - if (static_cast(v) < static_cast(minValue)) - { - *t = minValue; - } - else if (static_cast(v) > static_cast(maxValue)) - { - *t = maxValue; - } - else - { - *t = static_cast(v); - } - } - } - } - - - template - static void SetInternal(ImageAccessor& image, - int64_t constant) - { - for (unsigned int y = 0; y < image.GetHeight(); y++) - { - PixelType* p = reinterpret_cast(image.GetRow(y)); - - for (unsigned int x = 0; x < image.GetWidth(); x++, p++) - { - *p = static_cast(constant); - } - } - } - - - template - static void GetMinMaxValueInternal(PixelType& minValue, - PixelType& maxValue, - const ImageAccessor& source) - { - // Deal with the special case of empty image - if (source.GetWidth() == 0 || - source.GetHeight() == 0) - { - minValue = 0; - maxValue = 0; - return; - } - - minValue = std::numeric_limits::max(); - maxValue = std::numeric_limits::min(); - - for (unsigned int y = 0; y < source.GetHeight(); y++) - { - const PixelType* p = reinterpret_cast(source.GetConstRow(y)); - - for (unsigned int x = 0; x < source.GetWidth(); x++, p++) - { - if (*p < minValue) - { - minValue = *p; - } - - if (*p > maxValue) - { - maxValue = *p; - } - } - } - } - - - - template - static void AddConstantInternal(ImageAccessor& image, - int64_t constant) - { - if (constant == 0) - { - return; - } - - const int64_t minValue = std::numeric_limits::min(); - const int64_t maxValue = std::numeric_limits::max(); - - for (unsigned int y = 0; y < image.GetHeight(); y++) - { - PixelType* p = reinterpret_cast(image.GetRow(y)); - - for (unsigned int x = 0; x < image.GetWidth(); x++, p++) - { - int64_t v = static_cast(*p) + constant; - - if (v > maxValue) - { - *p = std::numeric_limits::max(); - } - else if (v < minValue) - { - *p = std::numeric_limits::min(); - } - else - { - *p = static_cast(v); - } - } - } - } - - - - template - void MultiplyConstantInternal(ImageAccessor& image, - float factor) - { - if (abs(factor - 1.0f) <= std::numeric_limits::epsilon()) - { - return; - } - - const int64_t minValue = std::numeric_limits::min(); - const int64_t maxValue = std::numeric_limits::max(); - - for (unsigned int y = 0; y < image.GetHeight(); y++) - { - PixelType* p = reinterpret_cast(image.GetRow(y)); - - for (unsigned int x = 0; x < image.GetWidth(); x++, p++) - { - int64_t v = boost::math::llround(static_cast(*p) * factor); - - if (v > maxValue) - { - *p = std::numeric_limits::max(); - } - else if (v < minValue) - { - *p = std::numeric_limits::min(); - } - else - { - *p = static_cast(v); - } - } - } - } - - - template - void ShiftScaleInternal(ImageAccessor& image, - float offset, - float scaling) - { - const float minValue = static_cast(std::numeric_limits::min()); - const float maxValue = static_cast(std::numeric_limits::max()); - - for (unsigned int y = 0; y < image.GetHeight(); y++) - { - PixelType* p = reinterpret_cast(image.GetRow(y)); - - for (unsigned int x = 0; x < image.GetWidth(); x++, p++) - { - float v = (static_cast(*p) + offset) * scaling; - - if (v > maxValue) - { - *p = std::numeric_limits::max(); - } - else if (v < minValue) - { - *p = std::numeric_limits::min(); - } - else - { - *p = static_cast(boost::math::iround(v)); - } - } - } - } - - - void ImageProcessing::Copy(ImageAccessor& target, - const ImageAccessor& source) - { - if (target.GetWidth() != source.GetWidth() || - target.GetHeight() != source.GetHeight()) - { - throw OrthancException(ErrorCode_IncompatibleImageSize); - } - - if (target.GetFormat() != source.GetFormat()) - { - throw OrthancException(ErrorCode_IncompatibleImageFormat); - } - - unsigned int lineSize = GetBytesPerPixel(source.GetFormat()) * source.GetWidth(); - - assert(source.GetPitch() >= lineSize && target.GetPitch() >= lineSize); - - for (unsigned int y = 0; y < source.GetHeight(); y++) - { - memcpy(target.GetRow(y), source.GetConstRow(y), lineSize); - } - } - - - void ImageProcessing::Convert(ImageAccessor& target, - const ImageAccessor& source) - { - if (target.GetWidth() != source.GetWidth() || - target.GetHeight() != source.GetHeight()) - { - throw OrthancException(ErrorCode_IncompatibleImageSize); - } - - if (source.GetFormat() == target.GetFormat()) - { - Copy(target, source); - return; - } - - if (target.GetFormat() == PixelFormat_Grayscale16 && - source.GetFormat() == PixelFormat_Grayscale8) - { - ConvertInternal(target, source); - return; - } - - if (target.GetFormat() == PixelFormat_SignedGrayscale16 && - source.GetFormat() == PixelFormat_Grayscale8) - { - ConvertInternal(target, source); - return; - } - - if (target.GetFormat() == PixelFormat_Grayscale8 && - source.GetFormat() == PixelFormat_Grayscale16) - { - ConvertInternal(target, source); - return; - } - - if (target.GetFormat() == PixelFormat_SignedGrayscale16 && - source.GetFormat() == PixelFormat_Grayscale16) - { - ConvertInternal(target, source); - return; - } - - if (target.GetFormat() == PixelFormat_Grayscale8 && - source.GetFormat() == PixelFormat_SignedGrayscale16) - { - ConvertInternal(target, source); - return; - } - - if (target.GetFormat() == PixelFormat_Grayscale16 && - source.GetFormat() == PixelFormat_SignedGrayscale16) - { - ConvertInternal(target, source); - return; - } - - if (target.GetFormat() == PixelFormat_Grayscale8 && - source.GetFormat() == PixelFormat_RGB24) - { - ConvertColorToGrayscale(target, source); - return; - } - - if (target.GetFormat() == PixelFormat_Grayscale16 && - source.GetFormat() == PixelFormat_RGB24) - { - ConvertColorToGrayscale(target, source); - return; - } - - if (target.GetFormat() == PixelFormat_SignedGrayscale16 && - source.GetFormat() == PixelFormat_RGB24) - { - ConvertColorToGrayscale(target, source); - return; - } - - throw OrthancException(ErrorCode_NotImplemented); - } - - - - void ImageProcessing::Set(ImageAccessor& image, - int64_t value) - { - switch (image.GetFormat()) - { - case PixelFormat_Grayscale8: - SetInternal(image, value); - return; - - case PixelFormat_Grayscale16: - SetInternal(image, value); - return; - - case PixelFormat_SignedGrayscale16: - SetInternal(image, value); - return; - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - } - - - void ImageProcessing::ShiftRight(ImageAccessor& image, - unsigned int shift) - { - if (image.GetWidth() == 0 || - image.GetHeight() == 0 || - shift == 0) - { - // Nothing to do - return; - } - - throw OrthancException(ErrorCode_NotImplemented); - } - - - void ImageProcessing::GetMinMaxValue(int64_t& minValue, - int64_t& maxValue, - const ImageAccessor& image) - { - switch (image.GetFormat()) - { - case PixelFormat_Grayscale8: - { - uint8_t a, b; - GetMinMaxValueInternal(a, b, image); - minValue = a; - maxValue = b; - break; - } - - case PixelFormat_Grayscale16: - { - uint16_t a, b; - GetMinMaxValueInternal(a, b, image); - minValue = a; - maxValue = b; - break; - } - - case PixelFormat_SignedGrayscale16: - { - int16_t a, b; - GetMinMaxValueInternal(a, b, image); - minValue = a; - maxValue = b; - break; - } - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - } - - - - void ImageProcessing::AddConstant(ImageAccessor& image, - int64_t value) - { - switch (image.GetFormat()) - { - case PixelFormat_Grayscale8: - AddConstantInternal(image, value); - return; - - case PixelFormat_Grayscale16: - AddConstantInternal(image, value); - return; - - case PixelFormat_SignedGrayscale16: - AddConstantInternal(image, value); - return; - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - } - - - void ImageProcessing::MultiplyConstant(ImageAccessor& image, - float factor) - { - switch (image.GetFormat()) - { - case PixelFormat_Grayscale8: - MultiplyConstantInternal(image, factor); - return; - - case PixelFormat_Grayscale16: - MultiplyConstantInternal(image, factor); - return; - - case PixelFormat_SignedGrayscale16: - MultiplyConstantInternal(image, factor); - return; - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - } - - - void ImageProcessing::ShiftScale(ImageAccessor& image, - float offset, - float scaling) - { - switch (image.GetFormat()) - { - case PixelFormat_Grayscale8: - ShiftScaleInternal(image, offset, scaling); - return; - - case PixelFormat_Grayscale16: - ShiftScaleInternal(image, offset, scaling); - return; - - case PixelFormat_SignedGrayscale16: - ShiftScaleInternal(image, offset, scaling); - return; - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/ImageFormats/ImageProcessing.h --- a/Core/ImageFormats/ImageProcessing.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ImageAccessor.h" - -#include - -namespace Orthanc -{ - class ImageProcessing - { - public: - static void Copy(ImageAccessor& target, - const ImageAccessor& source); - - static void Convert(ImageAccessor& target, - const ImageAccessor& source); - - static void Set(ImageAccessor& image, - int64_t value); - - static void ShiftRight(ImageAccessor& target, - unsigned int shift); - - static void GetMinMaxValue(int64_t& minValue, - int64_t& maxValue, - const ImageAccessor& image); - - static void AddConstant(ImageAccessor& image, - int64_t value); - - static void MultiplyConstant(ImageAccessor& image, - float factor); - - static void ShiftScale(ImageAccessor& image, - float offset, - float scaling); - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/ImageFormats/PngReader.cpp --- a/Core/ImageFormats/PngReader.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,313 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeaders.h" -#include "PngReader.h" - -#include "../OrthancException.h" -#include "../Toolbox.h" - -#include -#include // For memcpy() - -namespace Orthanc -{ - namespace - { - struct FileRabi - { - FILE* fp_; - - FileRabi(const char* filename) - { - fp_ = fopen(filename, "rb"); - if (!fp_) - { - throw OrthancException(ErrorCode_InexistentFile); - } - } - - ~FileRabi() - { - if (fp_) - fclose(fp_); - } - }; - } - - - struct PngReader::PngRabi - { - png_structp png_; - png_infop info_; - png_infop endInfo_; - - void Destruct() - { - if (png_) - { - png_destroy_read_struct(&png_, &info_, &endInfo_); - - png_ = NULL; - info_ = NULL; - endInfo_ = NULL; - } - } - - PngRabi() - { - png_ = NULL; - info_ = NULL; - endInfo_ = NULL; - - png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_) - { - throw OrthancException(ErrorCode_NotEnoughMemory); - } - - info_ = png_create_info_struct(png_); - if (!info_) - { - png_destroy_read_struct(&png_, NULL, NULL); - throw OrthancException(ErrorCode_NotEnoughMemory); - } - - endInfo_ = png_create_info_struct(png_); - if (!info_) - { - png_destroy_read_struct(&png_, &info_, NULL); - throw OrthancException(ErrorCode_NotEnoughMemory); - } - } - - ~PngRabi() - { - Destruct(); - } - - static void MemoryCallback(png_structp png_ptr, - png_bytep data, - png_size_t size); - }; - - - void PngReader::CheckHeader(const void* header) - { - int is_png = !png_sig_cmp((png_bytep) header, 0, 8); - if (!is_png) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - } - - PngReader::PngReader() - { - } - - void PngReader::Read(PngRabi& rabi) - { - png_set_sig_bytes(rabi.png_, 8); - - png_read_info(rabi.png_, rabi.info_); - - png_uint_32 width, height; - int bit_depth, color_type, interlace_type; - int compression_type, filter_method; - // get size and bit-depth of the PNG-image - png_get_IHDR(rabi.png_, rabi.info_, - &width, &height, - &bit_depth, &color_type, &interlace_type, - &compression_type, &filter_method); - - PixelFormat format; - unsigned int pitch; - - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 8) - { - format = PixelFormat_Grayscale8; - pitch = width; - } - else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16) - { - format = PixelFormat_Grayscale16; - pitch = 2 * width; - - if (Toolbox::DetectEndianness() == Endianness_Little) - { - png_set_swap(rabi.png_); - } - } - else if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8) - { - format = PixelFormat_RGB24; - pitch = 3 * width; - } - else if (color_type == PNG_COLOR_TYPE_RGBA && bit_depth == 8) - { - format = PixelFormat_RGBA32; - pitch = 4 * width; - } - else - { - throw OrthancException(ErrorCode_NotImplemented); - } - - data_.resize(height * pitch); - - if (height == 0 || width == 0) - { - // Empty image, we are done - AssignEmpty(format); - return; - } - - png_read_update_info(rabi.png_, rabi.info_); - - std::vector rows(height); - for (size_t i = 0; i < height; i++) - { - rows[i] = &data_[0] + i * pitch; - } - - png_read_image(rabi.png_, &rows[0]); - - AssignReadOnly(format, width, height, pitch, &data_[0]); - } - - void PngReader::ReadFromFile(const char* filename) - { - FileRabi f(filename); - - char header[8]; - if (fread(header, 1, 8, f.fp_) != 8) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - - CheckHeader(header); - - PngRabi rabi; - - if (setjmp(png_jmpbuf(rabi.png_))) - { - rabi.Destruct(); - throw OrthancException(ErrorCode_BadFileFormat); - } - - png_init_io(rabi.png_, f.fp_); - - Read(rabi); - } - - - namespace - { - struct MemoryBuffer - { - const uint8_t* buffer_; - size_t size_; - size_t pos_; - bool ok_; - }; - } - - - void PngReader::PngRabi::MemoryCallback(png_structp png_ptr, - png_bytep outBytes, - png_size_t byteCountToRead) - { - MemoryBuffer* from = reinterpret_cast(png_get_io_ptr(png_ptr)); - - if (!from->ok_) - { - return; - } - - if (from->pos_ + byteCountToRead > from->size_) - { - from->ok_ = false; - return; - } - - memcpy(outBytes, from->buffer_ + from->pos_, byteCountToRead); - - from->pos_ += byteCountToRead; - } - - - void PngReader::ReadFromMemory(const void* buffer, - size_t size) - { - if (size < 8) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - - CheckHeader(buffer); - - PngRabi rabi; - - if (setjmp(png_jmpbuf(rabi.png_))) - { - rabi.Destruct(); - throw OrthancException(ErrorCode_BadFileFormat); - } - - MemoryBuffer tmp; - tmp.buffer_ = reinterpret_cast(buffer) + 8; // We skip the header - tmp.size_ = size - 8; - tmp.pos_ = 0; - tmp.ok_ = true; - - png_set_read_fn(rabi.png_, &tmp, PngRabi::MemoryCallback); - - Read(rabi); - - if (!tmp.ok_) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - } - - void PngReader::ReadFromMemory(const std::string& buffer) - { - if (buffer.size() != 0) - { - ReadFromMemory(&buffer[0], buffer.size()); - } - else - { - ReadFromMemory(NULL, 0); - } - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/ImageFormats/PngReader.h --- a/Core/ImageFormats/PngReader.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ImageAccessor.h" - -#include "../Enumerations.h" - -#include -#include -#include - -namespace Orthanc -{ - class PngReader : public ImageAccessor - { - private: - struct PngRabi; - - std::vector data_; - - void CheckHeader(const void* header); - - void Read(PngRabi& rabi); - - public: - PngReader(); - - void ReadFromFile(const char* filename); - - void ReadFromFile(const std::string& filename) - { - ReadFromFile(filename.c_str()); - } - - void ReadFromMemory(const void* buffer, - size_t size); - - void ReadFromMemory(const std::string& buffer); - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/ImageFormats/PngWriter.cpp --- a/Core/ImageFormats/PngWriter.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,269 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeaders.h" -#include "PngWriter.h" - -#include -#include -#include -#include "../OrthancException.h" -#include "../ChunkedBuffer.h" -#include "../Toolbox.h" - - -// http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-4 -// http://zarb.org/~gc/html/libpng.html -/* - void write_row_callback(png_ptr, png_uint_32 row, int pass) - { - }*/ - - - - -/* bool isError_; - -// http://www.libpng.org/pub/png/book/chapter14.html#png.ch14.div.2 - -static void ErrorHandler(png_structp png, png_const_charp message) -{ -printf("** [%s]\n", message); - -PngWriter* that = (PngWriter*) png_get_error_ptr(png); -that->isError_ = true; -printf("** %d\n", (int)that); - -//((PngWriter*) payload)->isError_ = true; -} - -static void WarningHandler(png_structp png, png_const_charp message) -{ - printf("++ %d\n", (int)message); -}*/ - - -namespace Orthanc -{ - struct PngWriter::PImpl - { - png_structp png_; - png_infop info_; - - // Filled by Prepare() - std::vector rows_; - int bitDepth_; - int colorType_; - }; - - - - PngWriter::PngWriter() : pimpl_(new PImpl) - { - pimpl_->png_ = NULL; - pimpl_->info_ = NULL; - - pimpl_->png_ = png_create_write_struct - (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); //this, ErrorHandler, WarningHandler); - if (!pimpl_->png_) - { - throw OrthancException(ErrorCode_NotEnoughMemory); - } - - pimpl_->info_ = png_create_info_struct(pimpl_->png_); - if (!pimpl_->info_) - { - png_destroy_write_struct(&pimpl_->png_, NULL); - throw OrthancException(ErrorCode_NotEnoughMemory); - } - } - - PngWriter::~PngWriter() - { - if (pimpl_->info_) - { - png_destroy_info_struct(pimpl_->png_, &pimpl_->info_); - } - - if (pimpl_->png_) - { - png_destroy_write_struct(&pimpl_->png_, NULL); - } - } - - - - void PngWriter::Prepare(unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer) - { - pimpl_->rows_.resize(height); - for (unsigned int y = 0; y < height; y++) - { - pimpl_->rows_[y] = const_cast(reinterpret_cast(buffer)) + y * pitch; - } - - switch (format) - { - case PixelFormat_RGB24: - pimpl_->bitDepth_ = 8; - pimpl_->colorType_ = PNG_COLOR_TYPE_RGB; - break; - - case PixelFormat_RGBA32: - pimpl_->bitDepth_ = 8; - pimpl_->colorType_ = PNG_COLOR_TYPE_RGBA; - break; - - case PixelFormat_Grayscale8: - pimpl_->bitDepth_ = 8; - pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY; - break; - - case PixelFormat_Grayscale16: - case PixelFormat_SignedGrayscale16: - pimpl_->bitDepth_ = 16; - pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY; - break; - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - } - - - void PngWriter::Compress(unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format) - { - png_set_IHDR(pimpl_->png_, pimpl_->info_, width, height, - pimpl_->bitDepth_, pimpl_->colorType_, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - - png_write_info(pimpl_->png_, pimpl_->info_); - - if (height > 0) - { - switch (format) - { - case PixelFormat_Grayscale16: - case PixelFormat_SignedGrayscale16: - { - int transforms = 0; - if (Toolbox::DetectEndianness() == Endianness_Little) - { - transforms = PNG_TRANSFORM_SWAP_ENDIAN; - } - - png_set_rows(pimpl_->png_, pimpl_->info_, &pimpl_->rows_[0]); - png_write_png(pimpl_->png_, pimpl_->info_, transforms, NULL); - - break; - } - - default: - png_write_image(pimpl_->png_, &pimpl_->rows_[0]); - } - } - - png_write_end(pimpl_->png_, NULL); - } - - - void PngWriter::WriteToFile(const char* filename, - unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer) - { - Prepare(width, height, pitch, format, buffer); - - FILE* fp = fopen(filename, "wb"); - if (!fp) - { - throw OrthancException(ErrorCode_CannotWriteFile); - } - - png_init_io(pimpl_->png_, fp); - - if (setjmp(png_jmpbuf(pimpl_->png_))) - { - // Error during writing PNG - throw OrthancException(ErrorCode_CannotWriteFile); - } - - Compress(width, height, pitch, format); - - fclose(fp); - } - - - - - static void MemoryCallback(png_structp png_ptr, - png_bytep data, - png_size_t size) - { - ChunkedBuffer* buffer = reinterpret_cast(png_get_io_ptr(png_ptr)); - buffer->AddChunk(reinterpret_cast(data), size); - } - - - - void PngWriter::WriteToMemory(std::string& png, - unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer) - { - ChunkedBuffer chunks; - - Prepare(width, height, pitch, format, buffer); - - if (setjmp(png_jmpbuf(pimpl_->png_))) - { - // Error during writing PNG - throw OrthancException(ErrorCode_InternalError); - } - - png_set_write_fn(pimpl_->png_, &chunks, MemoryCallback, NULL); - - Compress(width, height, pitch, format); - - chunks.Flatten(png); - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/ImageFormats/PngWriter.h --- a/Core/ImageFormats/PngWriter.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ImageAccessor.h" - -#include -#include - -namespace Orthanc -{ - class PngWriter - { - private: - struct PImpl; - boost::shared_ptr pimpl_; - - void Compress(unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format); - - void Prepare(unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer); - - public: - PngWriter(); - - ~PngWriter(); - - void WriteToFile(const char* filename, - unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer); - - void WriteToMemory(std::string& png, - unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer); - - void WriteToFile(const char* filename, - const ImageAccessor& accessor) - { - WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(), - accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer()); - } - - void WriteToMemory(std::string& png, - const ImageAccessor& accessor) - { - WriteToMemory(png, accessor.GetWidth(), accessor.GetHeight(), - accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer()); - } - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/Font.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/Font.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,301 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "Font.h" + +#include "../Toolbox.h" +#include "../OrthancException.h" + +#include +#include +#include + +namespace Orthanc +{ + Font::~Font() + { + for (Characters::iterator it = characters_.begin(); + it != characters_.end(); ++it) + { + delete it->second; + } + } + + + void Font::LoadFromMemory(const std::string& font) + { + Json::Value v; + Json::Reader reader; + if (!reader.parse(font, v) || + v.type() != Json::objectValue || + !v.isMember("Name") || + !v.isMember("Size") || + !v.isMember("Characters") || + v["Name"].type() != Json::stringValue || + v["Size"].type() != Json::intValue || + v["Characters"].type() != Json::objectValue) + { + throw OrthancException(ErrorCode_BadFont); + } + + name_ = v["Name"].asString(); + size_ = v["Size"].asUInt(); + maxHeight_ = 0; + + Json::Value::Members characters = v["Characters"].getMemberNames(); + + for (size_t i = 0; i < characters.size(); i++) + { + const Json::Value& info = v["Characters"][characters[i]]; + if (info.type() != Json::objectValue || + !info.isMember("Advance") || + !info.isMember("Bitmap") || + !info.isMember("Height") || + !info.isMember("Top") || + !info.isMember("Width") || + info["Advance"].type() != Json::intValue || + info["Bitmap"].type() != Json::arrayValue || + info["Height"].type() != Json::intValue || + info["Top"].type() != Json::intValue || + info["Width"].type() != Json::intValue) + { + throw OrthancException(ErrorCode_BadFont); + } + + std::auto_ptr c(new Character); + + c->advance_ = info["Advance"].asUInt(); + c->height_ = info["Height"].asUInt(); + c->top_ = info["Top"].asUInt(); + c->width_ = info["Width"].asUInt(); + c->bitmap_.resize(info["Bitmap"].size()); + + if (c->height_ > maxHeight_) + { + maxHeight_ = c->height_; + } + + for (Json::Value::ArrayIndex j = 0; j < info["Bitmap"].size(); j++) + { + if (info["Bitmap"][j].type() != Json::intValue) + { + throw OrthancException(ErrorCode_BadFont); + } + + int value = info["Bitmap"][j].asInt(); + if (value < 0 || value > 255) + { + throw OrthancException(ErrorCode_BadFont); + } + + c->bitmap_[j] = static_cast(value); + } + + int index = boost::lexical_cast(characters[i]); + if (index < 0 || index > 255) + { + throw OrthancException(ErrorCode_BadFont); + } + + characters_[static_cast(index)] = c.release(); + } + } + + + void Font::LoadFromFile(const std::string& path) + { + std::string font; + Toolbox::ReadFile(font, path); + LoadFromMemory(font); + } + + + static unsigned int MyMin(unsigned int a, + unsigned int b) + { + return a < b ? a : b; + } + + + void Font::DrawCharacter(ImageAccessor& target, + const Character& character, + int x, + int y, + const uint8_t color[4]) const + { + // Compute the bounds of the character + if (x >= static_cast(target.GetWidth()) || + y >= static_cast(target.GetHeight())) + { + // The character is out of the image + return; + } + + unsigned int left = x < 0 ? -x : 0; + unsigned int top = y < 0 ? -y : 0; + unsigned int width = MyMin(character.width_, target.GetWidth() - x); + unsigned int height = MyMin(character.height_, target.GetHeight() - y); + + unsigned int bpp = target.GetBytesPerPixel(); + + // Blit the font bitmap OVER the target image + // https://en.wikipedia.org/wiki/Alpha_compositing + + for (unsigned int cy = top; cy < height; cy++) + { + uint8_t* p = reinterpret_cast(target.GetRow(y + cy)) + (x + left) * bpp; + unsigned int pos = cy * character.width_ + left; + + switch (target.GetFormat()) + { + case PixelFormat_Grayscale8: + { + assert(bpp == 1); + for (unsigned int cx = left; cx < width; cx++, pos++, p++) + { + uint16_t alpha = character.bitmap_[pos]; + uint16_t value = alpha * static_cast(color[0]) + (255 - alpha) * static_cast(*p); + *p = static_cast(value >> 8); + } + + break; + } + + case PixelFormat_RGB24: + { + assert(bpp == 3); + for (unsigned int cx = left; cx < width; cx++, pos++, p += 3) + { + uint16_t alpha = character.bitmap_[pos]; + for (uint8_t i = 0; i < 3; i++) + { + uint16_t value = alpha * static_cast(color[i]) + (255 - alpha) * static_cast(p[i]); + p[i] = static_cast(value >> 8); + } + } + + break; + } + + case PixelFormat_RGBA32: + { + assert(bpp == 4); + + for (unsigned int cx = left; cx < width; cx++, pos++, p += 4) + { + float alpha = static_cast(character.bitmap_[pos]) / 255.0f; + float beta = (1.0f - alpha) * static_cast(p[3]) / 255.0f; + float denom = 1.0f / (alpha + beta); + + for (uint8_t i = 0; i < 3; i++) + { + p[i] = static_cast((alpha * static_cast(color[i]) + + beta * static_cast(p[i])) * denom); + } + + p[3] = static_cast(255.0f * (alpha + beta)); + } + + break; + } + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + } + + + void Font::DrawInternal(ImageAccessor& target, + const std::string& utf8, + int x, + int y, + const uint8_t color[4]) const + { + if (target.GetFormat() != PixelFormat_Grayscale8 && + target.GetFormat() != PixelFormat_RGB24 && + target.GetFormat() != PixelFormat_RGBA32) + { + throw OrthancException(ErrorCode_NotImplemented); + } + + int a = x; + + std::string s = Toolbox::ConvertFromUtf8(utf8, Encoding_Latin1); + + for (size_t i = 0; i < s.size(); i++) + { + if (s[i] == '\n') + { + // Go to the next line + a = x; + y += maxHeight_ + 1; + } + else + { + Characters::const_iterator c = characters_.find(s[i]); + if (c != characters_.end()) + { + DrawCharacter(target, *c->second, a, y + static_cast(c->second->top_), color); + a += c->second->advance_; + } + } + } + } + + + void Font::Draw(ImageAccessor& target, + const std::string& utf8, + int x, + int y, + uint8_t grayscale) const + { + uint8_t color[4] = { grayscale, grayscale, grayscale, 255 }; + DrawInternal(target, utf8, x, y, color); + } + + + void Font::Draw(ImageAccessor& target, + const std::string& utf8, + int x, + int y, + uint8_t r, + uint8_t g, + uint8_t b) const + { + uint8_t color[4] = { r, g, b, 255 }; + DrawInternal(target, utf8, x, y, color); + } + +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/Font.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/Font.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,112 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ImageAccessor.h" + +#include +#include +#include +#include + +namespace Orthanc +{ + class Font : public boost::noncopyable + { + private: + struct Character + { + unsigned int width_; + unsigned int height_; + unsigned int top_; + unsigned int advance_; + std::vector bitmap_; + }; + + typedef std::map Characters; + + std::string name_; + unsigned int size_; + Characters characters_; + unsigned int maxHeight_; + + void DrawCharacter(ImageAccessor& target, + const Character& character, + int x, + int y, + const uint8_t color[4]) const; + + void DrawInternal(ImageAccessor& target, + const std::string& utf8, + int x, + int y, + const uint8_t color[4]) const; + + public: + Font() : + size_(0), + maxHeight_(0) + { + } + + ~Font(); + + void LoadFromMemory(const std::string& font); + + void LoadFromFile(const std::string& path); + + const std::string& GetName() const + { + return name_; + } + + unsigned int GetSize() const + { + return size_; + } + + void Draw(ImageAccessor& target, + const std::string& utf8, + int x, + int y, + uint8_t grayscale) const; + + void Draw(ImageAccessor& target, + const std::string& utf8, + int x, + int y, + uint8_t r, + uint8_t g, + uint8_t b) const; + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/FontRegistry.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/FontRegistry.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,86 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "FontRegistry.h" + +#include "../OrthancException.h" + +#include + +namespace Orthanc +{ + FontRegistry::~FontRegistry() + { + for (Fonts::iterator it = fonts_.begin(); it != fonts_.end(); ++it) + { + delete *it; + } + } + + + void FontRegistry::AddFromMemory(const std::string& font) + { + std::auto_ptr f(new Font); + f->LoadFromMemory(font); + fonts_.push_back(f.release()); + } + + + void FontRegistry::AddFromFile(const std::string& path) + { + std::auto_ptr f(new Font); + f->LoadFromFile(path); + fonts_.push_back(f.release()); + } + + + void FontRegistry::AddFromResource(EmbeddedResources::FileResourceId resource) + { + std::string content; + EmbeddedResources::GetFileResource(content, resource); + AddFromMemory(content); + } + + + const Font& FontRegistry::GetFont(size_t i) const + { + if (i >= fonts_.size()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + return *fonts_[i]; + } + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/FontRegistry.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/FontRegistry.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,64 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "Font.h" + +#include // Autogenerated file + +namespace Orthanc +{ + class FontRegistry : public boost::noncopyable + { + private: + typedef std::vector Fonts; + + Fonts fonts_; + + public: + ~FontRegistry(); + + void AddFromMemory(const std::string& font); + + void AddFromFile(const std::string& path); + + void AddFromResource(EmbeddedResources::FileResourceId resource); + + size_t GetSize() const + { + return fonts_.size(); + } + + const Font& GetFont(size_t i) const; + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/Image.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/Image.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,55 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ImageAccessor.h" +#include "ImageBuffer.h" + +namespace Orthanc +{ + class Image : public ImageAccessor + { + private: + ImageBuffer image_; + + public: + Image(PixelFormat format, + unsigned int width, + unsigned int height) : + image_(format, width, height) + { + ImageAccessor accessor = image_.GetAccessor(); + AssignWritable(format, width, height, accessor.GetPitch(), accessor.GetBuffer()); + } + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/ImageAccessor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/ImageAccessor.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,229 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "ImageAccessor.h" + +#include "../Logging.h" +#include "../OrthancException.h" +#include "../ChunkedBuffer.h" + +#include +#include +#include + + + +namespace Orthanc +{ + template + static void ToMatlabStringInternal(ChunkedBuffer& target, + const ImageAccessor& source) + { + target.AddChunk("double([ "); + + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + const PixelType* p = reinterpret_cast(source.GetConstRow(y)); + + std::string s; + if (y > 0) + { + s = "; "; + } + + s.reserve(source.GetWidth() * 8); + + for (unsigned int x = 0; x < source.GetWidth(); x++, p++) + { + s += boost::lexical_cast(static_cast(*p)) + " "; + } + + target.AddChunk(s); + } + + target.AddChunk("])"); + } + + + static void RGB24ToMatlabString(ChunkedBuffer& target, + const ImageAccessor& source) + { + assert(source.GetFormat() == PixelFormat_RGB24); + + target.AddChunk("double(permute(reshape([ "); + + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + const uint8_t* p = reinterpret_cast(source.GetConstRow(y)); + + std::string s; + s.reserve(source.GetWidth() * 3 * 8); + + for (unsigned int x = 0; x < 3 * source.GetWidth(); x++, p++) + { + s += boost::lexical_cast(static_cast(*p)) + " "; + } + + target.AddChunk(s); + } + + target.AddChunk("], [ 3 " + boost::lexical_cast(source.GetHeight()) + + " " + boost::lexical_cast(source.GetWidth()) + " ]), [ 3 2 1 ]))"); + } + + + void* ImageAccessor::GetBuffer() const + { + if (readOnly_) + { +#if ORTHANC_ENABLE_LOGGING == 1 + LOG(ERROR) << "Trying to write on a read-only image"; +#endif + + throw OrthancException(ErrorCode_ReadOnly); + } + + return buffer_; + } + + + const void* ImageAccessor::GetConstRow(unsigned int y) const + { + if (buffer_ != NULL) + { + return reinterpret_cast(buffer_) + y * pitch_; + } + else + { + return NULL; + } + } + + + void* ImageAccessor::GetRow(unsigned int y) const + { + if (readOnly_) + { +#if ORTHANC_ENABLE_LOGGING == 1 + LOG(ERROR) << "Trying to write on a read-only image"; +#endif + + throw OrthancException(ErrorCode_ReadOnly); + } + + if (buffer_ != NULL) + { + return reinterpret_cast(buffer_) + y * pitch_; + } + else + { + return NULL; + } + } + + + void ImageAccessor::AssignEmpty(PixelFormat format) + { + readOnly_ = false; + format_ = format; + width_ = 0; + height_ = 0; + pitch_ = 0; + buffer_ = NULL; + } + + + void ImageAccessor::AssignReadOnly(PixelFormat format, + unsigned int width, + unsigned int height, + unsigned int pitch, + const void *buffer) + { + readOnly_ = true; + format_ = format; + width_ = width; + height_ = height; + pitch_ = pitch; + buffer_ = const_cast(buffer); + + assert(GetBytesPerPixel() * width_ <= pitch_); + } + + + void ImageAccessor::AssignWritable(PixelFormat format, + unsigned int width, + unsigned int height, + unsigned int pitch, + void *buffer) + { + readOnly_ = false; + format_ = format; + width_ = width; + height_ = height; + pitch_ = pitch; + buffer_ = buffer; + + assert(GetBytesPerPixel() * width_ <= pitch_); + } + + + void ImageAccessor::ToMatlabString(std::string& target) const + { + ChunkedBuffer buffer; + + switch (GetFormat()) + { + case PixelFormat_Grayscale8: + ToMatlabStringInternal(buffer, *this); + break; + + case PixelFormat_Grayscale16: + ToMatlabStringInternal(buffer, *this); + break; + + case PixelFormat_SignedGrayscale16: + ToMatlabStringInternal(buffer, *this); + break; + + case PixelFormat_RGB24: + RGB24ToMatlabString(buffer, *this); + break; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + + buffer.Flatten(target); + } + +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/ImageAccessor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/ImageAccessor.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,123 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Enumerations.h" + +#include + +namespace Orthanc +{ + class ImageAccessor + { + private: + bool readOnly_; + PixelFormat format_; + unsigned int width_; + unsigned int height_; + unsigned int pitch_; + void *buffer_; + + public: + ImageAccessor() + { + AssignEmpty(PixelFormat_Grayscale8); + } + + virtual ~ImageAccessor() + { + } + + bool IsReadOnly() const + { + return readOnly_; + } + + PixelFormat GetFormat() const + { + return format_; + } + + unsigned int GetBytesPerPixel() const + { + return ::Orthanc::GetBytesPerPixel(format_); + } + + unsigned int GetWidth() const + { + return width_; + } + + unsigned int GetHeight() const + { + return height_; + } + + unsigned int GetPitch() const + { + return pitch_; + } + + unsigned int GetSize() const + { + return GetHeight() * GetPitch(); + } + + const void* GetConstBuffer() const + { + return buffer_; + } + + void* GetBuffer() const; + + const void* GetConstRow(unsigned int y) const; + + void* GetRow(unsigned int y) const; + + void AssignEmpty(PixelFormat format); + + void AssignReadOnly(PixelFormat format, + unsigned int width, + unsigned int height, + unsigned int pitch, + const void *buffer); + + void AssignWritable(PixelFormat format, + unsigned int width, + unsigned int height, + unsigned int pitch, + void *buffer); + + void ToMatlabString(std::string& target) const; + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/ImageBuffer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/ImageBuffer.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,192 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "ImageBuffer.h" + +#include "../OrthancException.h" + +#include +#include + +namespace Orthanc +{ + void ImageBuffer::Allocate() + { + if (changed_) + { + Deallocate(); + + /* + if (forceMinimalPitch_) + { + TODO: Align pitch and memory buffer to optimal size for SIMD. + } + */ + + pitch_ = GetBytesPerPixel() * width_; + size_t size = pitch_ * height_; + + if (size == 0) + { + buffer_ = NULL; + } + else + { + buffer_ = malloc(size); + if (buffer_ == NULL) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + } + + changed_ = false; + } + } + + + void ImageBuffer::Deallocate() + { + if (buffer_ != NULL) + { + free(buffer_); + buffer_ = NULL; + changed_ = true; + } + } + + + ImageBuffer::ImageBuffer(PixelFormat format, + unsigned int width, + unsigned int height) + { + Initialize(); + SetWidth(width); + SetHeight(height); + SetFormat(format); + } + + + void ImageBuffer::Initialize() + { + changed_ = false; + forceMinimalPitch_ = true; + format_ = PixelFormat_Grayscale8; + width_ = 0; + height_ = 0; + pitch_ = 0; + buffer_ = NULL; + } + + + void ImageBuffer::SetFormat(PixelFormat format) + { + if (format != format_) + { + changed_ = true; + format_ = format; + } + } + + + void ImageBuffer::SetWidth(unsigned int width) + { + if (width != width_) + { + changed_ = true; + width_ = width; + } + } + + + void ImageBuffer::SetHeight(unsigned int height) + { + if (height != height_) + { + changed_ = true; + height_ = height; + } + } + + + ImageAccessor ImageBuffer::GetAccessor() + { + Allocate(); + + ImageAccessor accessor; + accessor.AssignWritable(format_, width_, height_, pitch_, buffer_); + return accessor; + } + + + ImageAccessor ImageBuffer::GetConstAccessor() + { + Allocate(); + + ImageAccessor accessor; + accessor.AssignReadOnly(format_, width_, height_, pitch_, buffer_); + return accessor; + } + + + void ImageBuffer::SetMinimalPitchForced(bool force) + { + if (force != forceMinimalPitch_) + { + changed_ = true; + forceMinimalPitch_ = force; + } + } + + + void ImageBuffer::AcquireOwnership(ImageBuffer& other) + { + // Remove the content of the current image + Deallocate(); + + // Force the allocation of the other image (if not already + // allocated) + other.Allocate(); + + // Transfer the content of the other image + changed_ = false; + forceMinimalPitch_ = other.forceMinimalPitch_; + format_ = other.format_; + width_ = other.width_; + height_ = other.height_; + pitch_ = other.pitch_; + buffer_ = other.buffer_; + + // Force the reinitialization of the other image + other.Initialize(); + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/ImageBuffer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/ImageBuffer.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,115 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ImageAccessor.h" + +#include +#include +#include + +namespace Orthanc +{ + class ImageBuffer : public boost::noncopyable + { + private: + bool changed_; + + bool forceMinimalPitch_; // Currently unused + PixelFormat format_; + unsigned int width_; + unsigned int height_; + unsigned int pitch_; + void *buffer_; + + void Initialize(); + + void Allocate(); + + void Deallocate(); + + public: + ImageBuffer(PixelFormat format, + unsigned int width, + unsigned int height); + + ImageBuffer() + { + Initialize(); + } + + ~ImageBuffer() + { + Deallocate(); + } + + PixelFormat GetFormat() const + { + return format_; + } + + void SetFormat(PixelFormat format); + + unsigned int GetWidth() const + { + return width_; + } + + void SetWidth(unsigned int width); + + unsigned int GetHeight() const + { + return height_; + } + + void SetHeight(unsigned int height); + + unsigned int GetBytesPerPixel() const + { + return ::Orthanc::GetBytesPerPixel(format_); + } + + ImageAccessor GetAccessor(); + + ImageAccessor GetConstAccessor(); + + bool IsMinimalPitchForced() const + { + return forceMinimalPitch_; + } + + void SetMinimalPitchForced(bool force); + + void AcquireOwnership(ImageBuffer& other); + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/ImageProcessing.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/ImageProcessing.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,592 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "ImageProcessing.h" + +#include "../OrthancException.h" + +#include + +#include +#include +#include +#include + +namespace Orthanc +{ + template + static void ConvertInternal(ImageAccessor& target, + const ImageAccessor& source) + { + const TargetType minValue = std::numeric_limits::min(); + const TargetType maxValue = std::numeric_limits::max(); + + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + TargetType* t = reinterpret_cast(target.GetRow(y)); + const SourceType* s = reinterpret_cast(source.GetConstRow(y)); + + for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s++) + { + if (static_cast(*s) < static_cast(minValue)) + { + *t = minValue; + } + else if (static_cast(*s) > static_cast(maxValue)) + { + *t = maxValue; + } + else + { + *t = static_cast(*s); + } + } + } + } + + + template + static void ConvertColorToGrayscale(ImageAccessor& target, + const ImageAccessor& source) + { + assert(source.GetFormat() == PixelFormat_RGB24); + + const TargetType minValue = std::numeric_limits::min(); + const TargetType maxValue = std::numeric_limits::max(); + + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + TargetType* t = reinterpret_cast(target.GetRow(y)); + const uint8_t* s = reinterpret_cast(source.GetConstRow(y)); + + for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s += 3) + { + // Y = 0.2126 R + 0.7152 G + 0.0722 B + int32_t v = (2126 * static_cast(s[0]) + + 7152 * static_cast(s[1]) + + 0722 * static_cast(s[2])) / 1000; + + if (static_cast(v) < static_cast(minValue)) + { + *t = minValue; + } + else if (static_cast(v) > static_cast(maxValue)) + { + *t = maxValue; + } + else + { + *t = static_cast(v); + } + } + } + } + + + template + static void SetInternal(ImageAccessor& image, + int64_t constant) + { + for (unsigned int y = 0; y < image.GetHeight(); y++) + { + PixelType* p = reinterpret_cast(image.GetRow(y)); + + for (unsigned int x = 0; x < image.GetWidth(); x++, p++) + { + *p = static_cast(constant); + } + } + } + + + template + static void GetMinMaxValueInternal(PixelType& minValue, + PixelType& maxValue, + const ImageAccessor& source) + { + // Deal with the special case of empty image + if (source.GetWidth() == 0 || + source.GetHeight() == 0) + { + minValue = 0; + maxValue = 0; + return; + } + + minValue = std::numeric_limits::max(); + maxValue = std::numeric_limits::min(); + + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + const PixelType* p = reinterpret_cast(source.GetConstRow(y)); + + for (unsigned int x = 0; x < source.GetWidth(); x++, p++) + { + if (*p < minValue) + { + minValue = *p; + } + + if (*p > maxValue) + { + maxValue = *p; + } + } + } + } + + + + template + static void AddConstantInternal(ImageAccessor& image, + int64_t constant) + { + if (constant == 0) + { + return; + } + + const int64_t minValue = std::numeric_limits::min(); + const int64_t maxValue = std::numeric_limits::max(); + + for (unsigned int y = 0; y < image.GetHeight(); y++) + { + PixelType* p = reinterpret_cast(image.GetRow(y)); + + for (unsigned int x = 0; x < image.GetWidth(); x++, p++) + { + int64_t v = static_cast(*p) + constant; + + if (v > maxValue) + { + *p = std::numeric_limits::max(); + } + else if (v < minValue) + { + *p = std::numeric_limits::min(); + } + else + { + *p = static_cast(v); + } + } + } + } + + + + template + void MultiplyConstantInternal(ImageAccessor& image, + float factor) + { + if (std::abs(factor - 1.0f) <= std::numeric_limits::epsilon()) + { + return; + } + + const int64_t minValue = std::numeric_limits::min(); + const int64_t maxValue = std::numeric_limits::max(); + + for (unsigned int y = 0; y < image.GetHeight(); y++) + { + PixelType* p = reinterpret_cast(image.GetRow(y)); + + for (unsigned int x = 0; x < image.GetWidth(); x++, p++) + { + int64_t v = boost::math::llround(static_cast(*p) * factor); + + if (v > maxValue) + { + *p = std::numeric_limits::max(); + } + else if (v < minValue) + { + *p = std::numeric_limits::min(); + } + else + { + *p = static_cast(v); + } + } + } + } + + + template + void ShiftScaleInternal(ImageAccessor& image, + float offset, + float scaling) + { + const float minValue = static_cast(std::numeric_limits::min()); + const float maxValue = static_cast(std::numeric_limits::max()); + + for (unsigned int y = 0; y < image.GetHeight(); y++) + { + PixelType* p = reinterpret_cast(image.GetRow(y)); + + for (unsigned int x = 0; x < image.GetWidth(); x++, p++) + { + float v = (static_cast(*p) + offset) * scaling; + + if (v > maxValue) + { + *p = std::numeric_limits::max(); + } + else if (v < minValue) + { + *p = std::numeric_limits::min(); + } + else + { + *p = static_cast(boost::math::iround(v)); + } + } + } + } + + + void ImageProcessing::Copy(ImageAccessor& target, + const ImageAccessor& source) + { + if (target.GetWidth() != source.GetWidth() || + target.GetHeight() != source.GetHeight()) + { + throw OrthancException(ErrorCode_IncompatibleImageSize); + } + + if (target.GetFormat() != source.GetFormat()) + { + throw OrthancException(ErrorCode_IncompatibleImageFormat); + } + + unsigned int lineSize = GetBytesPerPixel(source.GetFormat()) * source.GetWidth(); + + assert(source.GetPitch() >= lineSize && target.GetPitch() >= lineSize); + + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + memcpy(target.GetRow(y), source.GetConstRow(y), lineSize); + } + } + + + void ImageProcessing::Convert(ImageAccessor& target, + const ImageAccessor& source) + { + if (target.GetWidth() != source.GetWidth() || + target.GetHeight() != source.GetHeight()) + { + throw OrthancException(ErrorCode_IncompatibleImageSize); + } + + if (source.GetFormat() == target.GetFormat()) + { + Copy(target, source); + return; + } + + if (target.GetFormat() == PixelFormat_Grayscale16 && + source.GetFormat() == PixelFormat_Grayscale8) + { + ConvertInternal(target, source); + return; + } + + if (target.GetFormat() == PixelFormat_SignedGrayscale16 && + source.GetFormat() == PixelFormat_Grayscale8) + { + ConvertInternal(target, source); + return; + } + + if (target.GetFormat() == PixelFormat_Grayscale8 && + source.GetFormat() == PixelFormat_Grayscale16) + { + ConvertInternal(target, source); + return; + } + + if (target.GetFormat() == PixelFormat_SignedGrayscale16 && + source.GetFormat() == PixelFormat_Grayscale16) + { + ConvertInternal(target, source); + return; + } + + if (target.GetFormat() == PixelFormat_Grayscale8 && + source.GetFormat() == PixelFormat_SignedGrayscale16) + { + ConvertInternal(target, source); + return; + } + + if (target.GetFormat() == PixelFormat_Grayscale16 && + source.GetFormat() == PixelFormat_SignedGrayscale16) + { + ConvertInternal(target, source); + return; + } + + if (target.GetFormat() == PixelFormat_Grayscale8 && + source.GetFormat() == PixelFormat_RGB24) + { + ConvertColorToGrayscale(target, source); + return; + } + + if (target.GetFormat() == PixelFormat_Grayscale16 && + source.GetFormat() == PixelFormat_RGB24) + { + ConvertColorToGrayscale(target, source); + return; + } + + if (target.GetFormat() == PixelFormat_SignedGrayscale16 && + source.GetFormat() == PixelFormat_RGB24) + { + ConvertColorToGrayscale(target, source); + return; + } + + if (target.GetFormat() == PixelFormat_Grayscale8 && + source.GetFormat() == PixelFormat_RGBA32) + { + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + const uint8_t* p = reinterpret_cast(source.GetConstRow(y)); + uint8_t* q = reinterpret_cast(target.GetRow(y)); + for (unsigned int x = 0; x < source.GetWidth(); x++, q++) + { + *q = static_cast((2126 * static_cast(p[0]) + + 7152 * static_cast(p[1]) + + 0722 * static_cast(p[2])) / 10000); + p += 4; + } + } + + return; + } + + if (target.GetFormat() == PixelFormat_RGB24 && + source.GetFormat() == PixelFormat_RGBA32) + { + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + const uint8_t* p = reinterpret_cast(source.GetConstRow(y)); + uint8_t* q = reinterpret_cast(target.GetRow(y)); + for (unsigned int x = 0; x < source.GetWidth(); x++) + { + q[0] = p[0]; + q[1] = p[1]; + q[2] = p[2]; + p += 4; + q += 3; + } + } + + return; + } + + if (target.GetFormat() == PixelFormat_RGBA32 && + source.GetFormat() == PixelFormat_RGB24) + { + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + const uint8_t* p = reinterpret_cast(source.GetConstRow(y)); + uint8_t* q = reinterpret_cast(target.GetRow(y)); + for (unsigned int x = 0; x < source.GetWidth(); x++) + { + q[0] = p[0]; + q[1] = p[1]; + q[2] = p[2]; + q[3] = 255; // Set the alpha channel to full opacity + p += 3; + q += 4; + } + } + + return; + } + + throw OrthancException(ErrorCode_NotImplemented); + } + + + + void ImageProcessing::Set(ImageAccessor& image, + int64_t value) + { + switch (image.GetFormat()) + { + case PixelFormat_Grayscale8: + SetInternal(image, value); + return; + + case PixelFormat_Grayscale16: + SetInternal(image, value); + return; + + case PixelFormat_SignedGrayscale16: + SetInternal(image, value); + return; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + void ImageProcessing::ShiftRight(ImageAccessor& image, + unsigned int shift) + { + if (image.GetWidth() == 0 || + image.GetHeight() == 0 || + shift == 0) + { + // Nothing to do + return; + } + + throw OrthancException(ErrorCode_NotImplemented); + } + + + void ImageProcessing::GetMinMaxValue(int64_t& minValue, + int64_t& maxValue, + const ImageAccessor& image) + { + switch (image.GetFormat()) + { + case PixelFormat_Grayscale8: + { + uint8_t a, b; + GetMinMaxValueInternal(a, b, image); + minValue = a; + maxValue = b; + break; + } + + case PixelFormat_Grayscale16: + { + uint16_t a, b; + GetMinMaxValueInternal(a, b, image); + minValue = a; + maxValue = b; + break; + } + + case PixelFormat_SignedGrayscale16: + { + int16_t a, b; + GetMinMaxValueInternal(a, b, image); + minValue = a; + maxValue = b; + break; + } + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + + void ImageProcessing::AddConstant(ImageAccessor& image, + int64_t value) + { + switch (image.GetFormat()) + { + case PixelFormat_Grayscale8: + AddConstantInternal(image, value); + return; + + case PixelFormat_Grayscale16: + AddConstantInternal(image, value); + return; + + case PixelFormat_SignedGrayscale16: + AddConstantInternal(image, value); + return; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + void ImageProcessing::MultiplyConstant(ImageAccessor& image, + float factor) + { + switch (image.GetFormat()) + { + case PixelFormat_Grayscale8: + MultiplyConstantInternal(image, factor); + return; + + case PixelFormat_Grayscale16: + MultiplyConstantInternal(image, factor); + return; + + case PixelFormat_SignedGrayscale16: + MultiplyConstantInternal(image, factor); + return; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + void ImageProcessing::ShiftScale(ImageAccessor& image, + float offset, + float scaling) + { + switch (image.GetFormat()) + { + case PixelFormat_Grayscale8: + ShiftScaleInternal(image, offset, scaling); + return; + + case PixelFormat_Grayscale16: + ShiftScaleInternal(image, offset, scaling); + return; + + case PixelFormat_SignedGrayscale16: + ShiftScaleInternal(image, offset, scaling); + return; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/ImageProcessing.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/ImageProcessing.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,70 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ImageAccessor.h" + +#include + +namespace Orthanc +{ + class ImageProcessing + { + public: + static void Copy(ImageAccessor& target, + const ImageAccessor& source); + + static void Convert(ImageAccessor& target, + const ImageAccessor& source); + + static void Set(ImageAccessor& image, + int64_t value); + + static void ShiftRight(ImageAccessor& target, + unsigned int shift); + + static void GetMinMaxValue(int64_t& minValue, + int64_t& maxValue, + const ImageAccessor& image); + + static void AddConstant(ImageAccessor& image, + int64_t value); + + static void MultiplyConstant(ImageAccessor& image, + float factor); + + static void ShiftScale(ImageAccessor& image, + float offset, + float scaling); + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/JpegErrorManager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/JpegErrorManager.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,69 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "JpegErrorManager.h" + +namespace Orthanc +{ + namespace Internals + { + void JpegErrorManager::OutputMessage(j_common_ptr cinfo) + { + char message[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message) (cinfo, message); + + JpegErrorManager* that = reinterpret_cast(cinfo->err); + that->message = std::string(message); + } + + + void JpegErrorManager::ErrorExit(j_common_ptr cinfo) + { + (*cinfo->err->output_message) (cinfo); + + JpegErrorManager* that = reinterpret_cast(cinfo->err); + longjmp(that->setjmp_buffer, 1); + } + + + JpegErrorManager::JpegErrorManager() + { + memset(&pub, 0, sizeof(struct jpeg_error_mgr)); + memset(&setjmp_buffer, 0, sizeof(jmp_buf)); + + jpeg_std_error(&pub); + pub.error_exit = ErrorExit; + pub.output_message = OutputMessage; + } + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/JpegErrorManager.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/JpegErrorManager.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,74 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Orthanc +{ + namespace Internals + { + class JpegErrorManager + { + private: + struct jpeg_error_mgr pub; /* "public" fields */ + jmp_buf setjmp_buffer; /* for return to caller */ + std::string message; + + static void OutputMessage(j_common_ptr cinfo); + + static void ErrorExit(j_common_ptr cinfo); + + public: + JpegErrorManager(); + + struct jpeg_error_mgr* GetPublic() + { + return &pub; + } + + jmp_buf& GetJumpBuffer() + { + return setjmp_buffer; + } + + const std::string& GetMessage() const + { + return message; + } + }; + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/JpegReader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/JpegReader.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,184 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "JpegReader.h" + +#include "JpegErrorManager.h" +#include "../OrthancException.h" +#include "../Logging.h" + +namespace Orthanc +{ + static void Uncompress(struct jpeg_decompress_struct& cinfo, + std::string& content, + ImageAccessor& accessor) + { + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + + PixelFormat format; + if (cinfo.output_components == 1 && + cinfo.out_color_space == JCS_GRAYSCALE) + { + format = PixelFormat_Grayscale8; + } + else if (cinfo.output_components == 3 && + cinfo.out_color_space == JCS_RGB) + { + format = PixelFormat_RGB24; + } + else + { + throw OrthancException(ErrorCode_NotImplemented); + } + + unsigned int pitch = cinfo.output_width * cinfo.output_components; + + /* Make a one-row-high sample array that will go away when done with image */ + JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, pitch, 1); + + try + { + content.resize(pitch * cinfo.output_height); + } + catch (...) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + accessor.AssignWritable(format, cinfo.output_width, cinfo.output_height, pitch, + content.empty() ? NULL : &content[0]); + + uint8_t* target = reinterpret_cast(&content[0]); + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines(&cinfo, buffer, 1); + memcpy(target, buffer[0], pitch); + target += pitch; + } + + // Everything went fine, "setjmp()" didn't get called + + jpeg_finish_decompress(&cinfo); + } + + + void JpegReader::ReadFromFile(const char* filename) + { + FILE* fp = fopen(filename, "rb"); + if (!fp) + { + throw OrthancException(ErrorCode_InexistentFile); + } + + struct jpeg_decompress_struct cinfo; + memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct)); + + Internals::JpegErrorManager jerr; + cinfo.err = jerr.GetPublic(); + + if (setjmp(jerr.GetJumpBuffer())) + { + jpeg_destroy_decompress(&cinfo); + fclose(fp); + LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage(); + throw OrthancException(ErrorCode_InternalError); + } + + // Below this line, we are under the scope of a "setjmp" + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, fp); + + try + { + Uncompress(cinfo, content_, *this); + } + catch (OrthancException&) + { + jpeg_destroy_decompress(&cinfo); + fclose(fp); + throw; + } + + jpeg_destroy_decompress(&cinfo); + fclose(fp); + } + + + void JpegReader::ReadFromMemory(const void* buffer, + size_t size) + { + struct jpeg_decompress_struct cinfo; + memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct)); + + Internals::JpegErrorManager jerr; + cinfo.err = jerr.GetPublic(); + + if (setjmp(jerr.GetJumpBuffer())) + { + jpeg_destroy_decompress(&cinfo); + LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage(); + throw OrthancException(ErrorCode_InternalError); + } + + // Below this line, we are under the scope of a "setjmp" + jpeg_create_decompress(&cinfo); + jpeg_mem_src(&cinfo, const_cast(reinterpret_cast(buffer)), size); + + try + { + Uncompress(cinfo, content_, *this); + } + catch (OrthancException&) + { + jpeg_destroy_decompress(&cinfo); + throw; + } + + jpeg_destroy_decompress(&cinfo); + } + + + void JpegReader::ReadFromMemory(const std::string& buffer) + { + if (buffer.empty()) + { + ReadFromMemory(NULL, 0); + } + else + { + ReadFromMemory(buffer.c_str(), buffer.size()); + } + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/JpegReader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/JpegReader.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,59 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ImageAccessor.h" + +#include + +namespace Orthanc +{ + class JpegReader : public ImageAccessor + { + private: + std::string content_; + + public: + void ReadFromFile(const char* filename); + + void ReadFromFile(const std::string& filename) + { + ReadFromFile(filename.c_str()); + } + + void ReadFromMemory(const void* buffer, + size_t size); + + void ReadFromMemory(const std::string& buffer); + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/JpegWriter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/JpegWriter.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,202 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "JpegWriter.h" + +#include "../OrthancException.h" +#include "../Logging.h" + +#include "JpegErrorManager.h" + +#include + +namespace Orthanc +{ + static void GetLines(std::vector& lines, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer) + { + if (format != PixelFormat_Grayscale8 && + format != PixelFormat_RGB24) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + lines.resize(height); + + uint8_t* base = const_cast(reinterpret_cast(buffer)); + for (unsigned int y = 0; y < height; y++) + { + lines[y] = base + static_cast(y) * static_cast(pitch); + } + } + + + static void Compress(struct jpeg_compress_struct& cinfo, + std::vector& lines, + unsigned int width, + unsigned int height, + PixelFormat format, + uint8_t quality) + { + cinfo.image_width = width; + cinfo.image_height = height; + + switch (format) + { + case PixelFormat_Grayscale8: + cinfo.input_components = 1; + cinfo.in_color_space = JCS_GRAYSCALE; + break; + + case PixelFormat_RGB24: + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE); + jpeg_start_compress(&cinfo, TRUE); + jpeg_write_scanlines(&cinfo, &lines[0], height); + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + } + + + void JpegWriter::SetQuality(uint8_t quality) + { + if (quality <= 0 || quality > 100) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + quality_ = quality; + } + + + void JpegWriter::WriteToFile(const char* filename, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer) + { + FILE* fp = fopen(filename, "wb"); + if (fp == NULL) + { + throw OrthancException(ErrorCode_FullStorage); + } + + std::vector lines; + GetLines(lines, height, pitch, format, buffer); + + struct jpeg_compress_struct cinfo; + memset(&cinfo, 0, sizeof(struct jpeg_compress_struct)); + + Internals::JpegErrorManager jerr; + cinfo.err = jerr.GetPublic(); + + if (setjmp(jerr.GetJumpBuffer())) + { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + jpeg_destroy_compress(&cinfo); + fclose(fp); + LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage(); + throw OrthancException(ErrorCode_InternalError); + } + + // Do not allocate data on the stack below this line! + + jpeg_create_compress(&cinfo); + jpeg_stdio_dest(&cinfo, fp); + Compress(cinfo, lines, width, height, format, quality_); + + // Everything went fine, "setjmp()" didn't get called + + fclose(fp); + } + + + void JpegWriter::WriteToMemory(std::string& jpeg, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer) + { + std::vector lines; + GetLines(lines, height, pitch, format, buffer); + + struct jpeg_compress_struct cinfo; + memset(&cinfo, 0, sizeof(struct jpeg_compress_struct)); + + Internals::JpegErrorManager jerr; + + unsigned char* data = NULL; + unsigned long size; + + if (setjmp(jerr.GetJumpBuffer())) + { + jpeg_destroy_compress(&cinfo); + + if (data != NULL) + { + free(data); + } + + LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage(); + throw OrthancException(ErrorCode_InternalError); + } + + // Do not allocate data on the stack below this line! + + jpeg_create_compress(&cinfo); + cinfo.err = jerr.GetPublic(); + jpeg_mem_dest(&cinfo, &data, &size); + + Compress(cinfo, lines, width, height, format, quality_); + + // Everything went fine, "setjmp()" didn't get called + + jpeg.assign(reinterpret_cast(data), size); + free(data); + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/JpegWriter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/JpegWriter.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,87 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ImageAccessor.h" + +#include +#include + +namespace Orthanc +{ + class JpegWriter + { + private: + uint8_t quality_; + + public: + JpegWriter() : quality_(90) + { + } + + void SetQuality(uint8_t quality); + + uint8_t GetQuality() const + { + return quality_; + } + + void WriteToFile(const char* filename, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer); + + void WriteToMemory(std::string& jpeg, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer); + + void WriteToFile(const char* filename, + const ImageAccessor& accessor) + { + WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(), + accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer()); + } + + void WriteToMemory(std::string& jpeg, + const ImageAccessor& accessor) + { + WriteToMemory(jpeg, accessor.GetWidth(), accessor.GetHeight(), + accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer()); + } + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/PngReader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/PngReader.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,313 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "PngReader.h" + +#include "../OrthancException.h" +#include "../Toolbox.h" + +#include +#include // For memcpy() + +namespace Orthanc +{ + namespace + { + struct FileRabi + { + FILE* fp_; + + FileRabi(const char* filename) + { + fp_ = fopen(filename, "rb"); + if (!fp_) + { + throw OrthancException(ErrorCode_InexistentFile); + } + } + + ~FileRabi() + { + if (fp_) + fclose(fp_); + } + }; + } + + + struct PngReader::PngRabi + { + png_structp png_; + png_infop info_; + png_infop endInfo_; + + void Destruct() + { + if (png_) + { + png_destroy_read_struct(&png_, &info_, &endInfo_); + + png_ = NULL; + info_ = NULL; + endInfo_ = NULL; + } + } + + PngRabi() + { + png_ = NULL; + info_ = NULL; + endInfo_ = NULL; + + png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + info_ = png_create_info_struct(png_); + if (!info_) + { + png_destroy_read_struct(&png_, NULL, NULL); + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + endInfo_ = png_create_info_struct(png_); + if (!info_) + { + png_destroy_read_struct(&png_, &info_, NULL); + throw OrthancException(ErrorCode_NotEnoughMemory); + } + } + + ~PngRabi() + { + Destruct(); + } + + static void MemoryCallback(png_structp png_ptr, + png_bytep data, + png_size_t size); + }; + + + void PngReader::CheckHeader(const void* header) + { + int is_png = !png_sig_cmp((png_bytep) header, 0, 8); + if (!is_png) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + + PngReader::PngReader() + { + } + + void PngReader::Read(PngRabi& rabi) + { + png_set_sig_bytes(rabi.png_, 8); + + png_read_info(rabi.png_, rabi.info_); + + png_uint_32 width, height; + int bit_depth, color_type, interlace_type; + int compression_type, filter_method; + // get size and bit-depth of the PNG-image + png_get_IHDR(rabi.png_, rabi.info_, + &width, &height, + &bit_depth, &color_type, &interlace_type, + &compression_type, &filter_method); + + PixelFormat format; + unsigned int pitch; + + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 8) + { + format = PixelFormat_Grayscale8; + pitch = width; + } + else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16) + { + format = PixelFormat_Grayscale16; + pitch = 2 * width; + + if (Toolbox::DetectEndianness() == Endianness_Little) + { + png_set_swap(rabi.png_); + } + } + else if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8) + { + format = PixelFormat_RGB24; + pitch = 3 * width; + } + else if (color_type == PNG_COLOR_TYPE_RGBA && bit_depth == 8) + { + format = PixelFormat_RGBA32; + pitch = 4 * width; + } + else + { + throw OrthancException(ErrorCode_NotImplemented); + } + + data_.resize(height * pitch); + + if (height == 0 || width == 0) + { + // Empty image, we are done + AssignEmpty(format); + return; + } + + png_read_update_info(rabi.png_, rabi.info_); + + std::vector rows(height); + for (size_t i = 0; i < height; i++) + { + rows[i] = &data_[0] + i * pitch; + } + + png_read_image(rabi.png_, &rows[0]); + + AssignWritable(format, width, height, pitch, &data_[0]); + } + + void PngReader::ReadFromFile(const char* filename) + { + FileRabi f(filename); + + char header[8]; + if (fread(header, 1, 8, f.fp_) != 8) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + CheckHeader(header); + + PngRabi rabi; + + if (setjmp(png_jmpbuf(rabi.png_))) + { + rabi.Destruct(); + throw OrthancException(ErrorCode_BadFileFormat); + } + + png_init_io(rabi.png_, f.fp_); + + Read(rabi); + } + + + namespace + { + struct MemoryBuffer + { + const uint8_t* buffer_; + size_t size_; + size_t pos_; + bool ok_; + }; + } + + + void PngReader::PngRabi::MemoryCallback(png_structp png_ptr, + png_bytep outBytes, + png_size_t byteCountToRead) + { + MemoryBuffer* from = reinterpret_cast(png_get_io_ptr(png_ptr)); + + if (!from->ok_) + { + return; + } + + if (from->pos_ + byteCountToRead > from->size_) + { + from->ok_ = false; + return; + } + + memcpy(outBytes, from->buffer_ + from->pos_, byteCountToRead); + + from->pos_ += byteCountToRead; + } + + + void PngReader::ReadFromMemory(const void* buffer, + size_t size) + { + if (size < 8) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + CheckHeader(buffer); + + PngRabi rabi; + + if (setjmp(png_jmpbuf(rabi.png_))) + { + rabi.Destruct(); + throw OrthancException(ErrorCode_BadFileFormat); + } + + MemoryBuffer tmp; + tmp.buffer_ = reinterpret_cast(buffer) + 8; // We skip the header + tmp.size_ = size - 8; + tmp.pos_ = 0; + tmp.ok_ = true; + + png_set_read_fn(rabi.png_, &tmp, PngRabi::MemoryCallback); + + Read(rabi); + + if (!tmp.ok_) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + + void PngReader::ReadFromMemory(const std::string& buffer) + { + if (buffer.size() != 0) + { + ReadFromMemory(&buffer[0], buffer.size()); + } + else + { + ReadFromMemory(NULL, 0); + } + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/PngReader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/PngReader.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,71 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ImageAccessor.h" + +#include "../Enumerations.h" + +#include +#include +#include + +namespace Orthanc +{ + class PngReader : public ImageAccessor + { + private: + struct PngRabi; + + std::vector data_; + + void CheckHeader(const void* header); + + void Read(PngRabi& rabi); + + public: + PngReader(); + + void ReadFromFile(const char* filename); + + void ReadFromFile(const std::string& filename) + { + ReadFromFile(filename.c_str()); + } + + void ReadFromMemory(const void* buffer, + size_t size); + + void ReadFromMemory(const std::string& buffer); + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/PngWriter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/PngWriter.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,269 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "PngWriter.h" + +#include +#include +#include +#include "../OrthancException.h" +#include "../ChunkedBuffer.h" +#include "../Toolbox.h" + + +// http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-4 +// http://zarb.org/~gc/html/libpng.html +/* + void write_row_callback(png_ptr, png_uint_32 row, int pass) + { + }*/ + + + + +/* bool isError_; + +// http://www.libpng.org/pub/png/book/chapter14.html#png.ch14.div.2 + +static void ErrorHandler(png_structp png, png_const_charp message) +{ +printf("** [%s]\n", message); + +PngWriter* that = (PngWriter*) png_get_error_ptr(png); +that->isError_ = true; +printf("** %d\n", (int)that); + +//((PngWriter*) payload)->isError_ = true; +} + +static void WarningHandler(png_structp png, png_const_charp message) +{ + printf("++ %d\n", (int)message); +}*/ + + +namespace Orthanc +{ + struct PngWriter::PImpl + { + png_structp png_; + png_infop info_; + + // Filled by Prepare() + std::vector rows_; + int bitDepth_; + int colorType_; + }; + + + + PngWriter::PngWriter() : pimpl_(new PImpl) + { + pimpl_->png_ = NULL; + pimpl_->info_ = NULL; + + pimpl_->png_ = png_create_write_struct + (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); //this, ErrorHandler, WarningHandler); + if (!pimpl_->png_) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + pimpl_->info_ = png_create_info_struct(pimpl_->png_); + if (!pimpl_->info_) + { + png_destroy_write_struct(&pimpl_->png_, NULL); + throw OrthancException(ErrorCode_NotEnoughMemory); + } + } + + PngWriter::~PngWriter() + { + if (pimpl_->info_) + { + png_destroy_info_struct(pimpl_->png_, &pimpl_->info_); + } + + if (pimpl_->png_) + { + png_destroy_write_struct(&pimpl_->png_, NULL); + } + } + + + + void PngWriter::Prepare(unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer) + { + pimpl_->rows_.resize(height); + for (unsigned int y = 0; y < height; y++) + { + pimpl_->rows_[y] = const_cast(reinterpret_cast(buffer)) + y * pitch; + } + + switch (format) + { + case PixelFormat_RGB24: + pimpl_->bitDepth_ = 8; + pimpl_->colorType_ = PNG_COLOR_TYPE_RGB; + break; + + case PixelFormat_RGBA32: + pimpl_->bitDepth_ = 8; + pimpl_->colorType_ = PNG_COLOR_TYPE_RGBA; + break; + + case PixelFormat_Grayscale8: + pimpl_->bitDepth_ = 8; + pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY; + break; + + case PixelFormat_Grayscale16: + case PixelFormat_SignedGrayscale16: + pimpl_->bitDepth_ = 16; + pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY; + break; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + void PngWriter::Compress(unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format) + { + png_set_IHDR(pimpl_->png_, pimpl_->info_, width, height, + pimpl_->bitDepth_, pimpl_->colorType_, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_write_info(pimpl_->png_, pimpl_->info_); + + if (height > 0) + { + switch (format) + { + case PixelFormat_Grayscale16: + case PixelFormat_SignedGrayscale16: + { + int transforms = 0; + if (Toolbox::DetectEndianness() == Endianness_Little) + { + transforms = PNG_TRANSFORM_SWAP_ENDIAN; + } + + png_set_rows(pimpl_->png_, pimpl_->info_, &pimpl_->rows_[0]); + png_write_png(pimpl_->png_, pimpl_->info_, transforms, NULL); + + break; + } + + default: + png_write_image(pimpl_->png_, &pimpl_->rows_[0]); + } + } + + png_write_end(pimpl_->png_, NULL); + } + + + void PngWriter::WriteToFile(const char* filename, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer) + { + Prepare(width, height, pitch, format, buffer); + + FILE* fp = fopen(filename, "wb"); + if (!fp) + { + throw OrthancException(ErrorCode_CannotWriteFile); + } + + png_init_io(pimpl_->png_, fp); + + if (setjmp(png_jmpbuf(pimpl_->png_))) + { + // Error during writing PNG + throw OrthancException(ErrorCode_CannotWriteFile); + } + + Compress(width, height, pitch, format); + + fclose(fp); + } + + + + + static void MemoryCallback(png_structp png_ptr, + png_bytep data, + png_size_t size) + { + ChunkedBuffer* buffer = reinterpret_cast(png_get_io_ptr(png_ptr)); + buffer->AddChunk(reinterpret_cast(data), size); + } + + + + void PngWriter::WriteToMemory(std::string& png, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer) + { + ChunkedBuffer chunks; + + Prepare(width, height, pitch, format, buffer); + + if (setjmp(png_jmpbuf(pimpl_->png_))) + { + // Error during writing PNG + throw OrthancException(ErrorCode_InternalError); + } + + png_set_write_fn(pimpl_->png_, &chunks, MemoryCallback, NULL); + + Compress(width, height, pitch, format); + + chunks.Flatten(png); + } +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Images/PngWriter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/PngWriter.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,92 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ImageAccessor.h" + +#include +#include + +namespace Orthanc +{ + class PngWriter + { + private: + struct PImpl; + boost::shared_ptr pimpl_; + + void Compress(unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format); + + void Prepare(unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer); + + public: + PngWriter(); + + ~PngWriter(); + + void WriteToFile(const char* filename, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer); + + void WriteToMemory(std::string& png, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer); + + void WriteToFile(const char* filename, + const ImageAccessor& accessor) + { + WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(), + accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer()); + } + + void WriteToMemory(std::string& png, + const ImageAccessor& accessor) + { + WriteToMemory(png, accessor.GetWidth(), accessor.GetHeight(), + accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer()); + } + }; +} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Logging.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Logging.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,421 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "PrecompiledHeaders.h" +#include "Logging.h" + +#if ORTHANC_ENABLE_LOGGING != 1 + +namespace Orthanc +{ + namespace Logging + { + void Initialize() + { + } + + void Finalize() + { + } + + void EnableInfoLevel(bool enabled) + { + } + + void EnableTraceLevel(bool enabled) + { + } + + void SetTargetFolder(const std::string& path) + { + } + } +} + +#elif ORTHANC_ENABLE_GOOGLE_LOG == 1 + +/********************************************************* + * Wrapper around Google Log + *********************************************************/ + +namespace Orthanc +{ + namespace Logging + { + void Initialize() + { + // Initialize Google's logging library. + FLAGS_logtostderr = true; + FLAGS_minloglevel = 1; // Do not print LOG(INFO) by default + FLAGS_v = 0; // Do not print trace-level VLOG(1) by default + + google::InitGoogleLogging("Orthanc"); + } + + void Finalize() + { + google::ShutdownGoogleLogging(); + } + + void EnableInfoLevel(bool enabled) + { + FLAGS_minloglevel = (enabled ? 0 : 1); + } + + void EnableTraceLevel(bool enabled) + { + if (enabled) + { + FLAGS_minloglevel = 0; + FLAGS_v = 1; + } + else + { + FLAGS_v = 0; + } + } + + void SetTargetFolder(const std::string& path) + { + FLAGS_logtostderr = false; + FLAGS_log_dir = path; + } + } +} + +#else + +/********************************************************* + * Use internal logger, not Google Log + *********************************************************/ + +#include "OrthancException.h" +#include "Enumerations.h" +#include "Toolbox.h" + +#include +#include +#include + +#if BOOST_HAS_DATE_TIME == 1 +# include +#else +# error Boost::date_time is required +#endif + + +namespace +{ + struct LoggingState + { + bool infoEnabled_; + bool traceEnabled_; + + std::ostream* error_; + std::ostream* warning_; + std::ostream* info_; + + std::auto_ptr errorFile_; + std::auto_ptr warningFile_; + std::auto_ptr infoFile_; + + LoggingState() : + infoEnabled_(false), + traceEnabled_(false), + error_(&std::cerr), + warning_(&std::cerr), + info_(&std::cerr) + { + } + }; +} + + + +static std::auto_ptr loggingState_; +static boost::mutex loggingMutex_; + + + +namespace Orthanc +{ + namespace Logging + { + static void GetLogPath(boost::filesystem::path& log, + boost::filesystem::path& link, + const char* level, + const std::string& directory) + { + /** + From Google Log documentation: + + Unless otherwise specified, logs will be written to the filename + "...log..", + followed by the date, time, and pid (you can't prevent the date, + time, and pid from being in the filename). + + In this implementation : "hostname" and "username" are not used + **/ + + boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + boost::filesystem::path root(directory); + boost::filesystem::path exe(Toolbox::GetPathToExecutable()); + + if (!boost::filesystem::exists(root) || + !boost::filesystem::is_directory(root)) + { + throw OrthancException(ErrorCode_CannotWriteFile); + } + + char date[64]; + sprintf(date, "%04d%02d%02d-%02d%02d%02d.%d", + static_cast(now.date().year()), + now.date().month().as_number(), + now.date().day().as_number(), + now.time_of_day().hours(), + now.time_of_day().minutes(), + now.time_of_day().seconds(), + Toolbox::GetProcessId()); + + std::string programName = exe.filename().replace_extension("").string(); + + log = (root / (programName + ".log." + + std::string(level) + "." + + std::string(date))); + + link = (root / (programName + "." + std::string(level))); + } + + + static void PrepareLogFile(std::ostream*& stream, + std::auto_ptr& file, + const char* level, + const std::string& directory) + { + boost::filesystem::path log, link; + GetLogPath(log, link, level, directory); + +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) + boost::filesystem::remove(link); + boost::filesystem::create_symlink(log.filename(), link); +#endif + + file.reset(new std::ofstream(log.string().c_str())); + stream = file.get(); + } + + + void Initialize() + { + boost::mutex::scoped_lock lock(loggingMutex_); + loggingState_.reset(new LoggingState); + } + + void Finalize() + { + boost::mutex::scoped_lock lock(loggingMutex_); + loggingState_.reset(NULL); + } + + void EnableInfoLevel(bool enabled) + { + boost::mutex::scoped_lock lock(loggingMutex_); + assert(loggingState_.get() != NULL); + + loggingState_->infoEnabled_ = enabled; + } + + void EnableTraceLevel(bool enabled) + { + boost::mutex::scoped_lock lock(loggingMutex_); + assert(loggingState_.get() != NULL); + + loggingState_->traceEnabled_ = enabled; + + if (enabled) + { + // Also enable the "INFO" level when trace-level debugging is enabled + loggingState_->infoEnabled_ = true; + } + } + + void SetTargetFolder(const std::string& path) + { + boost::mutex::scoped_lock lock(loggingMutex_); + assert(loggingState_.get() != NULL); + + PrepareLogFile(loggingState_->error_, loggingState_->errorFile_, "ERROR", path); + PrepareLogFile(loggingState_->warning_, loggingState_->warningFile_, "WARNING", path); + PrepareLogFile(loggingState_->info_, loggingState_->infoFile_, "INFO", path); + } + + InternalLogger::InternalLogger(const char* level, + const char* file, + int line) : + lock_(loggingMutex_), + stream_(&null_) // By default, logging to "/dev/null" is simulated + { + if (loggingState_.get() == NULL) + { + fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n"); + return; + } + + LogLevel l = StringToLogLevel(level); + + if ((l == LogLevel_Info && !loggingState_->infoEnabled_) || + (l == LogLevel_Trace && !loggingState_->traceEnabled_)) + { + // This logging level is disabled, directly exit and unlock + // the mutex to speed-up things. The stream is set to "/dev/null" + lock_.unlock(); + return; + } + + // Compute the header of the line, temporary release the lock as + // this is a time-consuming operation + lock_.unlock(); + std::string header; + + { + boost::filesystem::path path(file); + boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration duration = now.time_of_day(); + + /** + From Google Log documentation: + + "Log lines have this form: + + Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... + + where the fields are defined as follows: + + L A single character, representing the log level (eg 'I' for INFO) + mm The month (zero padded; ie May is '05') + dd The day (zero padded) + hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds + threadid The space-padded thread ID as returned by GetTID() (this matches the PID on Linux) + file The file name + line The line number + msg The user-supplied message" + + In this implementation, "threadid" is not printed. + **/ + + char date[32]; + sprintf(date, "%c%02d%02d %02d:%02d:%02d.%06d ", + level[0], + now.date().month().as_number(), + now.date().day().as_number(), + duration.hours(), + duration.minutes(), + duration.seconds(), + static_cast(duration.fractional_seconds())); + + header = std::string(date) + path.filename().string() + ":" + boost::lexical_cast(line) + "] "; + } + + + // The header is computed, we now re-lock the mutex to access + // the stream objects. Pay attention that "loggingState_", + // "infoEnabled_" or "traceEnabled_" might have changed while + // the mutex was unlocked. + lock_.lock(); + + if (loggingState_.get() == NULL) + { + fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n"); + return; + } + + switch (l) + { + case LogLevel_Error: + stream_ = loggingState_->error_; + break; + + case LogLevel_Warning: + stream_ = loggingState_->warning_; + break; + + case LogLevel_Info: + if (loggingState_->infoEnabled_) + { + stream_ = loggingState_->info_; + } + + break; + + case LogLevel_Trace: + if (loggingState_->traceEnabled_) + { + stream_ = loggingState_->info_; + } + + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + if (stream_ == &null_) + { + // The logging is disabled for this level. The stream is the + // "null_" member of this object, so we can release the global + // mutex. + lock_.unlock(); + } + + (*stream_) << header; + } + + + InternalLogger::~InternalLogger() + { + if (stream_ != &null_) + { +#if defined(_WIN32) + *stream_ << "\r\n"; +#else + *stream_ << "\n"; +#endif + + stream_->flush(); + } + } + + + } +} + +#endif // ORTHANC_ENABLE_LOGGING diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Logging.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Logging.h Wed Sep 30 13:23:31 2015 +0200 @@ -0,0 +1,112 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include + +namespace Orthanc +{ + namespace Logging + { + void Initialize(); + + void Finalize(); + + void EnableInfoLevel(bool enabled); + + void EnableTraceLevel(bool enabled); + + void SetTargetFolder(const std::string& path); + + struct NullStream : public std::ostream + { + NullStream() : + std::ios(0), + std::ostream(0) + { + } + + std::ostream& operator<< (const std::string& message) + { + return *this; + } + }; + } +} + + +#if ORTHANC_ENABLE_LOGGING != 1 + +# define LOG(level) ::Orthanc::Logging::NullStream() +# define VLOG(level) ::Orthanc::Logging::NullStream() + +#else /* ORTHANC_ENABLE_LOGGING == 1 */ + +#if ORTHANC_ENABLE_GOOGLE_LOG == 1 +# include // Including this fixes a problem in glog for recent releases of MinGW +# include +#else +# include +# define LOG(level) ::Orthanc::Logging::InternalLogger(#level, __FILE__, __LINE__) +# define VLOG(level) ::Orthanc::Logging::InternalLogger("TRACE", __FILE__, __LINE__) +#endif + +#if ORTHANC_ENABLE_GOOGLE_LOG != 1 +namespace Orthanc +{ + namespace Logging + { + class InternalLogger + { + private: + boost::mutex::scoped_lock lock_; + NullStream null_; + std::ostream* stream_; + + public: + InternalLogger(const char* level, + const char* file, + int line); + + ~InternalLogger(); + + std::ostream& operator<< (const std::string& message) + { + return (*stream_) << message; + } + }; + } +} +#endif + +#endif // ORTHANC_ENABLE_LOGGING diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Lua/LuaContext.cpp --- a/Core/Lua/LuaContext.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Lua/LuaContext.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -33,8 +33,12 @@ #include "../PrecompiledHeaders.h" #include "LuaContext.h" -#include +#include "../Logging.h" +#include "../OrthancException.h" + +#include #include +#include extern "C" { @@ -44,16 +48,26 @@ namespace Orthanc { + static bool OnlyContainsDigits(const std::string& s) + { + for (size_t i = 0; i < s.size(); i++) + { + if (!isdigit(s[i])) + { + return false; + } + } + + return true; + } + + LuaContext& LuaContext::GetLuaContext(lua_State *state) { - // Get the pointer to the "LuaContext" underlying object - lua_getglobal(state, "_LuaContext"); - assert(lua_type(state, -1) == LUA_TLIGHTUSERDATA); - LuaContext* that = const_cast(reinterpret_cast(lua_topointer(state, -1))); - assert(that != NULL); - lua_pop(state, 1); + const void* value = GetGlobalVariable(state, "_LuaContext"); + assert(value != NULL); - return *that; + return *const_cast(reinterpret_cast(value)); } int LuaContext::PrintToLog(lua_State *state) @@ -94,6 +108,64 @@ } + int LuaContext::ParseJson(lua_State *state) + { + LuaContext& that = GetLuaContext(state); + + int nArgs = lua_gettop(state); + if (nArgs != 1 || + !lua_isstring(state, 1)) // Password + { + lua_pushnil(state); + return 1; + } + + const char* str = lua_tostring(state, 1); + + Json::Value value; + Json::Reader reader; + if (reader.parse(str, str + strlen(str), value)) + { + that.PushJson(value); + } + else + { + lua_pushnil(state); + } + + return 1; + } + + + int LuaContext::DumpJson(lua_State *state) + { + LuaContext& that = GetLuaContext(state); + + int nArgs = lua_gettop(state); + if ((nArgs != 1 && nArgs != 2) || + (nArgs == 2 && !lua_isboolean(state, 2))) + { + lua_pushnil(state); + return 1; + } + + bool keepStrings = false; + if (nArgs == 2) + { + keepStrings = lua_toboolean(state, 2) ? true : false; + } + + Json::Value json; + that.GetJson(json, 1, keepStrings); + + Json::FastWriter writer; + std::string s = writer.write(json); + lua_pushlstring(state, s.c_str(), s.size()); + + return 1; + } + + int LuaContext::SetHttpCredentials(lua_State *state) { LuaContext& that = GetLuaContext(state); @@ -126,13 +198,13 @@ { httpClient_.Apply(str); } - catch (OrthancException& e) + catch (OrthancException&) { return false; } // Return the result of the HTTP request - lua_pushstring(state, str.c_str()); + lua_pushlstring(state, str.c_str(), str.size()); return true; } @@ -147,7 +219,7 @@ if (nArgs != 1 || !lua_isstring(state, 1)) // URL { LOG(ERROR) << "Lua: Bad parameters to HttpGet()"; - lua_pushstring(state, "ERROR"); + lua_pushnil(state); return 1; } @@ -160,7 +232,7 @@ if (!that.AnswerHttpQuery(state)) { LOG(ERROR) << "Lua: Error in HttpGet() for URL " << url; - lua_pushstring(state, "ERROR"); + lua_pushnil(state); } return 1; @@ -179,7 +251,7 @@ (nArgs >= 2 && !lua_isstring(state, 2))) // Body data { LOG(ERROR) << "Lua: Bad parameters to HttpPost() or HttpPut()"; - lua_pushstring(state, "ERROR"); + lua_pushnil(state); return 1; } @@ -190,18 +262,18 @@ if (nArgs >= 2) { - that.httpClient_.SetPostData(lua_tostring(state, 2)); + that.httpClient_.SetBody(lua_tostring(state, 2)); } else { - that.httpClient_.AccessPostData().clear(); + that.httpClient_.GetBody().clear(); } // Do the HTTP POST/PUT request if (!that.AnswerHttpQuery(state)) { LOG(ERROR) << "Lua: Error in HttpPost() or HttpPut() for URL " << url; - lua_pushstring(state, "ERROR"); + lua_pushnil(state); } return 1; @@ -229,7 +301,7 @@ if (nArgs != 1 || !lua_isstring(state, 1)) // URL { LOG(ERROR) << "Lua: Bad parameters to HttpDelete()"; - lua_pushstring(state, "ERROR"); + lua_pushnil(state); return 1; } @@ -243,7 +315,7 @@ if (!that.httpClient_.Apply(s)) { LOG(ERROR) << "Lua: Error in HttpDelete() for URL " << url; - lua_pushstring(state, "ERROR"); + lua_pushnil(state); } else { @@ -258,7 +330,8 @@ { if (value.isString()) { - lua_pushstring(lua_, value.asCString()); + const std::string s = value.asString(); + lua_pushlstring(lua_, s.c_str(), s.size()); } else if (value.isDouble()) { @@ -307,7 +380,7 @@ it = members.begin(); it != members.end(); ++it) { // Push the index of the cell - lua_pushstring(lua_, it->c_str()); + lua_pushlstring(lua_, it->c_str(), it->size()); // Push the value of the cell PushJson(value[*it]); @@ -318,7 +391,116 @@ } else { - throw LuaException("Unsupported JSON conversion"); + throw OrthancException(ErrorCode_JsonToLuaTable); + } + } + + + void LuaContext::GetJson(Json::Value& result, + int top, + bool keepStrings) + { + if (lua_istable(lua_, top)) + { + Json::Value tmp = Json::objectValue; + bool isArray = true; + size_t size = 0; + + // Code adapted from: http://stackoverflow.com/a/6142700/881731 + + // Push another reference to the table on top of the stack (so we know + // where it is, and this function can work for negative, positive and + // pseudo indices + lua_pushvalue(lua_, top); + // stack now contains: -1 => table + lua_pushnil(lua_); + // stack now contains: -1 => nil; -2 => table + while (lua_next(lua_, -2)) + { + // stack now contains: -1 => value; -2 => key; -3 => table + // copy the key so that lua_tostring does not modify the original + lua_pushvalue(lua_, -2); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + std::string key(lua_tostring(lua_, -1)); + Json::Value v; + GetJson(v, -2, keepStrings); + + tmp[key] = v; + + size += 1; + try + { + if (!OnlyContainsDigits(key) || + boost::lexical_cast(key) != size) + { + isArray = false; + } + } + catch (boost::bad_lexical_cast&) + { + isArray = false; + } + + // pop value + copy of key, leaving original key + lua_pop(lua_, 2); + // stack now contains: -1 => key; -2 => table + } + // stack now contains: -1 => table (when lua_next returns 0 it pops the key + // but does not push anything.) + // Pop table + lua_pop(lua_, 1); + + // Stack is now the same as it was on entry to this function + + if (isArray) + { + result = Json::arrayValue; + for (size_t i = 0; i < size; i++) + { + result.append(tmp[boost::lexical_cast(i + 1)]); + } + } + else + { + result = tmp; + } + } + else if (lua_isnil(lua_, top)) + { + result = Json::nullValue; + } + else if (!keepStrings && + lua_isboolean(lua_, top)) + { + result = lua_toboolean(lua_, top) ? true : false; + } + else if (!keepStrings && + lua_isnumber(lua_, top)) + { + // Convert to "int" if truncation does not loose precision + double value = static_cast(lua_tonumber(lua_, top)); + int truncated = static_cast(value); + + if (std::abs(value - static_cast(truncated)) <= + std::numeric_limits::epsilon()) + { + result = truncated; + } + else + { + result = value; + } + } + else if (lua_isstring(lua_, top)) + { + // Caution: The "lua_isstring()" case must be the last, since + // Lua can convert most types to strings by default. + result = std::string(lua_tostring(lua_, top)); + } + else + { + LOG(WARNING) << "Unsupported Lua type when returning Json"; + result = Json::nullValue; } } @@ -328,19 +510,20 @@ lua_ = luaL_newstate(); if (!lua_) { - throw LuaException("Unable to create the Lua context"); + throw OrthancException(ErrorCode_CannotCreateLua); } luaL_openlibs(lua_); lua_register(lua_, "print", PrintToLog); + lua_register(lua_, "ParseJson", ParseJson); + lua_register(lua_, "DumpJson", DumpJson); lua_register(lua_, "HttpGet", CallHttpGet); lua_register(lua_, "HttpPost", CallHttpPost); lua_register(lua_, "HttpPut", CallHttpPut); lua_register(lua_, "HttpDelete", CallHttpDelete); lua_register(lua_, "SetHttpCredentials", SetHttpCredentials); - - lua_pushlightuserdata(lua_, this); - lua_setglobal(lua_, "_LuaContext"); + + SetGlobalVariable("_LuaContext", this); } @@ -364,7 +547,7 @@ std::string description(lua_tostring(lua_, -1)); lua_pop(lua_, 1); /* pop error message from the stack */ LOG(ERROR) << "Error while executing Lua script: " << description; - throw LuaException(description); + throw OrthancException(ErrorCode_CannotExecuteLua); } if (output != NULL) @@ -399,8 +582,33 @@ Json::Reader reader; if (!reader.parse(s, output)) { - throw OrthancException(ErrorCode_BadFileFormat); + throw OrthancException(ErrorCode_BadJson); } } + + void LuaContext::RegisterFunction(const char* name, + lua_CFunction func) + { + lua_register(lua_, name, func); + } + + + void LuaContext::SetGlobalVariable(const char* name, + void* value) + { + lua_pushlightuserdata(lua_, value); + lua_setglobal(lua_, name); + } + + + const void* LuaContext::GetGlobalVariable(lua_State* state, + const char* name) + { + lua_getglobal(state, name); + assert(lua_type(state, -1) == LUA_TLIGHTUSERDATA); + const void* value = lua_topointer(state, -1); + lua_pop(state, 1); + return value; + } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Lua/LuaContext.h --- a/Core/Lua/LuaContext.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Lua/LuaContext.h Wed Sep 30 13:23:31 2015 +0200 @@ -32,7 +32,6 @@ #pragma once -#include "LuaException.h" #include "../HttpClient.h" extern "C" @@ -54,9 +53,9 @@ std::string log_; HttpClient httpClient_; - static LuaContext& GetLuaContext(lua_State *state); - static int PrintToLog(lua_State *state); + static int ParseJson(lua_State *state); + static int DumpJson(lua_State *state); static int SetHttpCredentials(lua_State *state); @@ -72,7 +71,9 @@ void ExecuteInternal(std::string* output, const std::string& command); - void PushJson(const Json::Value& value); + void GetJson(Json::Value& result, + int top, + bool keepStrings); public: LuaContext(); @@ -107,5 +108,18 @@ { httpClient_.SetProxy(proxy); } + + void RegisterFunction(const char* name, + lua_CFunction func); + + void SetGlobalVariable(const char* name, + void* value); + + static LuaContext& GetLuaContext(lua_State *state); + + static const void* GetGlobalVariable(lua_State* state, + const char* name); + + void PushJson(const Json::Value& value); }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Lua/LuaException.h --- a/Core/Lua/LuaException.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../OrthancException.h" - -namespace Orthanc -{ - class LuaException : public OrthancException - { - public: - LuaException(const char* explanation) : - OrthancException(explanation) - { - } - - LuaException(const std::string& explanation) : - OrthancException(explanation) - { - } - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Lua/LuaFunctionCall.cpp --- a/Core/Lua/LuaFunctionCall.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Lua/LuaFunctionCall.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -33,10 +33,12 @@ #include "../PrecompiledHeaders.h" #include "LuaFunctionCall.h" +#include "../OrthancException.h" +#include "../Logging.h" + #include #include #include -#include namespace Orthanc { @@ -44,7 +46,7 @@ { if (isExecuted_) { - throw LuaException("Arguments cannot be pushed after the function is executed"); + throw OrthancException(ErrorCode_LuaAlreadyExecuted); } } @@ -61,7 +63,7 @@ void LuaFunctionCall::PushString(const std::string& value) { CheckAlreadyExecuted(); - lua_pushstring(context_.lua_, value.c_str()); + lua_pushlstring(context_.lua_, value.c_str(), value.size()); } void LuaFunctionCall::PushBoolean(bool value) @@ -102,12 +104,14 @@ std::string description(lua_tostring(context_.lua_, -1)); lua_pop(context_.lua_, 1); /* pop error message from the stack */ - throw LuaException(description); + LOG(ERROR) << description; + + throw OrthancException(ErrorCode_CannotExecuteLua); } if (lua_gettop(context_.lua_) < numOutputs) { - throw LuaException("The function does not give the expected number of outputs"); + throw OrthancException(ErrorCode_LuaBadOutput); } isExecuted_ = true; @@ -119,104 +123,33 @@ if (!lua_isboolean(context_.lua_, 1)) { - throw LuaException("The function is not a predicate (only true/false outputs allowed)"); + throw OrthancException(ErrorCode_NotLuaPredicate); } return lua_toboolean(context_.lua_, 1) != 0; } - static void PopJson(Json::Value& result, - lua_State* lua, - int top) + void LuaFunctionCall::ExecuteToJson(Json::Value& result, + bool keepStrings) { - if (lua_istable(lua, top)) - { - Json::Value tmp = Json::objectValue; - bool isArray = true; - size_t size = 0; + ExecuteInternal(1); + context_.GetJson(result, lua_gettop(context_.lua_), keepStrings); + } - // http://stackoverflow.com/a/6142700/881731 - - // Push another reference to the table on top of the stack (so we know - // where it is, and this function can work for negative, positive and - // pseudo indices - lua_pushvalue(lua, top); - // stack now contains: -1 => table - lua_pushnil(lua); - // stack now contains: -1 => nil; -2 => table - while (lua_next(lua, -2)) - { - // stack now contains: -1 => value; -2 => key; -3 => table - // copy the key so that lua_tostring does not modify the original - lua_pushvalue(lua, -2); - // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table - std::string key(lua_tostring(lua, -1)); - Json::Value v; - PopJson(v, lua, -2); - - tmp[key] = v; - size += 1; - try - { - if (boost::lexical_cast(key) != size) - { - isArray = false; - } - } - catch (boost::bad_lexical_cast&) - { - isArray = false; - } - - // pop value + copy of key, leaving original key - lua_pop(lua, 2); - // stack now contains: -1 => key; -2 => table - } - // stack now contains: -1 => table (when lua_next returns 0 it pops the key - // but does not push anything.) - // Pop table - lua_pop(lua, 1); - - // Stack is now the same as it was on entry to this function - - if (isArray) - { - result = Json::arrayValue; - for (size_t i = 0; i < size; i++) - { - result.append(tmp[boost::lexical_cast(i + 1)]); - } - } - else - { - result = tmp; - } - } - else if (lua_isnumber(lua, top)) + void LuaFunctionCall::ExecuteToString(std::string& result) + { + ExecuteInternal(1); + + int top = lua_gettop(context_.lua_); + if (lua_isstring(context_.lua_, top)) { - result = static_cast(lua_tonumber(lua, top)); - } - else if (lua_isstring(lua, top)) - { - result = std::string(lua_tostring(lua, top)); - } - else if (lua_isboolean(lua, top)) - { - result = static_cast(lua_toboolean(lua, top)); + result = lua_tostring(context_.lua_, top); } else { - LOG(WARNING) << "Unsupported Lua type when returning Json"; - result = Json::nullValue; + throw OrthancException(ErrorCode_LuaReturnsNoString); } } - - - void LuaFunctionCall::ExecuteToJson(Json::Value& result) - { - ExecuteInternal(1); - PopJson(result, context_.lua_, lua_gettop(context_.lua_)); - } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Lua/LuaFunctionCall.h --- a/Core/Lua/LuaFunctionCall.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Lua/LuaFunctionCall.h Wed Sep 30 13:23:31 2015 +0200 @@ -69,6 +69,9 @@ bool ExecutePredicate(); - void ExecuteToJson(Json::Value& result); + void ExecuteToJson(Json::Value& result, + bool keepStrings); + + void ExecuteToString(std::string& result); }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/MultiThreading/ArrayFilledByThreads.cpp --- a/Core/MultiThreading/ArrayFilledByThreads.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,154 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeaders.h" -#include "ArrayFilledByThreads.h" - -#include "../MultiThreading/ThreadedCommandProcessor.h" -#include "../OrthancException.h" - -namespace Orthanc -{ - class ArrayFilledByThreads::Command : public ICommand - { - private: - ArrayFilledByThreads& that_; - size_t index_; - - public: - Command(ArrayFilledByThreads& that, - size_t index) : - that_(that), - index_(index) - { - } - - virtual bool Execute() - { - std::auto_ptr obj(that_.filler_.GetFillerItem(index_)); - if (obj.get() == NULL) - { - return false; - } - else - { - boost::mutex::scoped_lock lock(that_.mutex_); - that_.array_[index_] = obj.release(); - return true; - } - } - }; - - void ArrayFilledByThreads::Clear() - { - for (size_t i = 0; i < array_.size(); i++) - { - if (array_[i]) - delete array_[i]; - } - - array_.clear(); - filled_ = false; - } - - void ArrayFilledByThreads::Update() - { - if (!filled_) - { - array_.resize(filler_.GetFillerSize()); - - Orthanc::ThreadedCommandProcessor processor(threadCount_); - for (size_t i = 0; i < array_.size(); i++) - { - processor.Post(new Command(*this, i)); - } - - processor.Join(); - filled_ = true; - } - } - - - ArrayFilledByThreads::ArrayFilledByThreads(IFiller& filler) : filler_(filler) - { - filled_ = false; - threadCount_ = 4; - } - - - ArrayFilledByThreads::~ArrayFilledByThreads() - { - Clear(); - } - - - void ArrayFilledByThreads::Reload() - { - Clear(); - Update(); - } - - - void ArrayFilledByThreads::Invalidate() - { - Clear(); - } - - - void ArrayFilledByThreads::SetThreadCount(unsigned int t) - { - if (t < 1) - { - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - - threadCount_ = t; - } - - - size_t ArrayFilledByThreads::GetSize() - { - Update(); - return array_.size(); - } - - - IDynamicObject& ArrayFilledByThreads::GetItem(size_t index) - { - if (index >= GetSize()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - return *array_[index]; - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/MultiThreading/ArrayFilledByThreads.h --- a/Core/MultiThreading/ArrayFilledByThreads.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -#pragma once - -#include - -#include "../IDynamicObject.h" - -namespace Orthanc -{ - class ArrayFilledByThreads - { - public: - class IFiller - { - public: - virtual size_t GetFillerSize() = 0; - - virtual IDynamicObject* GetFillerItem(size_t index) = 0; - }; - - private: - IFiller& filler_; - boost::mutex mutex_; - std::vector array_; - bool filled_; - unsigned int threadCount_; - - class Command; - - void Clear(); - - void Update(); - - public: - ArrayFilledByThreads(IFiller& filler); - - ~ArrayFilledByThreads(); - - void Reload(); - - void Invalidate(); - - void SetThreadCount(unsigned int t); - - unsigned int GetThreadCount() const - { - return threadCount_; - } - - size_t GetSize(); - - IDynamicObject& GetItem(size_t index); - }; -} - diff -r 61ce8147f30d -r 4a5c79e31b60 Core/MultiThreading/BagOfRunnablesBySteps.cpp --- a/Core/MultiThreading/BagOfRunnablesBySteps.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/MultiThreading/BagOfRunnablesBySteps.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -33,6 +33,8 @@ #include "../PrecompiledHeaders.h" #include "BagOfRunnablesBySteps.h" +#include "../Logging.h" + #include #include @@ -128,15 +130,10 @@ BagOfRunnablesBySteps::~BagOfRunnablesBySteps() { - StopAll(); - - // Stop the finish listener - pimpl_->stopFinishListener_ = true; - pimpl_->oneThreadIsStopped_.notify_one(); // Awakens the listener - - if (pimpl_->finishListener_->joinable()) + if (!pimpl_->stopFinishListener_) { - pimpl_->finishListener_->join(); + LOG(ERROR) << "INTERNAL ERROR: BagOfRunnablesBySteps::Finalize() should be invoked manually to avoid mess in the destruction order!"; + Finalize(); } } @@ -165,4 +162,24 @@ pimpl_->continue_ = true; } + + + + void BagOfRunnablesBySteps::Finalize() + { + if (!pimpl_->stopFinishListener_) + { + StopAll(); + + // Stop the finish listener + pimpl_->stopFinishListener_ = true; + pimpl_->oneThreadIsStopped_.notify_one(); // Awakens the listener + + if (pimpl_->finishListener_->joinable()) + { + pimpl_->finishListener_->join(); + } + } + } + } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/MultiThreading/BagOfRunnablesBySteps.h --- a/Core/MultiThreading/BagOfRunnablesBySteps.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/MultiThreading/BagOfRunnablesBySteps.h Wed Sep 30 13:23:31 2015 +0200 @@ -58,5 +58,7 @@ void Add(IRunnableBySteps* runnable); void StopAll(); + + void Finalize(); }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/MultiThreading/Mutex.cpp --- a/Core/MultiThreading/Mutex.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/MultiThreading/Mutex.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -37,7 +37,7 @@ #if defined(_WIN32) #include -#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__) +#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__FreeBSD__) #include #else #error Support your platform here @@ -75,7 +75,7 @@ } -#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__) +#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__FreeBSD__) struct Mutex::PImpl { diff -r 61ce8147f30d -r 4a5c79e31b60 Core/MultiThreading/Semaphore.cpp --- a/Core/MultiThreading/Semaphore.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/MultiThreading/Semaphore.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -30,6 +30,7 @@ **/ +#include "../PrecompiledHeaders.h" #include "Semaphore.h" #include "../OrthancException.h" diff -r 61ce8147f30d -r 4a5c79e31b60 Core/MultiThreading/ThreadedCommandProcessor.cpp --- a/Core/MultiThreading/ThreadedCommandProcessor.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,211 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeaders.h" -#include "ThreadedCommandProcessor.h" - -#include "../OrthancException.h" - -namespace Orthanc -{ - static const int32_t TIMEOUT = 10; - - - void ThreadedCommandProcessor::Processor(ThreadedCommandProcessor* that) - { - while (!that->done_) - { - std::auto_ptr command(that->queue_.Dequeue(TIMEOUT)); - - if (command.get() != NULL) - { - bool success = false; - - try - { - if (that->success_) - { - // No command has failed so far - - if (that->cancel_) - { - // The commands have been canceled. Skip the execution - // of this command, yet mark it as succeeded. - success = true; - } - else - { - success = dynamic_cast(*command).Execute(); - } - } - else - { - // A command has already failed. Skip the execution of this command. - } - } - catch (OrthancException) - { - } - - { - boost::mutex::scoped_lock lock(that->mutex_); - assert(that->remainingCommands_ > 0); - that->remainingCommands_--; - - if (!success) - { - if (!that->cancel_ && that->listener_ && that->success_) - { - // This is the first command that fails - that->listener_->SignalFailure(); - } - - that->success_ = false; - } - else - { - if (!that->cancel_ && that->listener_) - { - if (that->remainingCommands_ == 0) - { - that->listener_->SignalSuccess(that->totalCommands_); - } - else - { - that->listener_->SignalProgress(that->totalCommands_ - that->remainingCommands_, - that->totalCommands_); - } - } - } - - that->processedCommand_.notify_all(); - } - } - } - } - - - ThreadedCommandProcessor::ThreadedCommandProcessor(unsigned int numThreads) - { - if (numThreads < 1) - { - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - - listener_ = NULL; - success_ = true; - done_ = false; - cancel_ = false; - threads_.resize(numThreads); - remainingCommands_ = 0; - totalCommands_ = 0; - - for (unsigned int i = 0; i < numThreads; i++) - { - threads_[i] = new boost::thread(Processor, this); - } - } - - - ThreadedCommandProcessor::~ThreadedCommandProcessor() - { - done_ = true; - - for (unsigned int i = 0; i < threads_.size(); i++) - { - boost::thread* t = threads_[i]; - - if (t != NULL) - { - if (t->joinable()) - { - t->join(); - } - - delete t; - } - } - } - - - void ThreadedCommandProcessor::Post(ICommand* command) - { - if (command == NULL) - { - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - - boost::mutex::scoped_lock lock(mutex_); - queue_.Enqueue(command); - remainingCommands_++; - totalCommands_++; - } - - - bool ThreadedCommandProcessor::Join() - { - boost::mutex::scoped_lock lock(mutex_); - - while (!remainingCommands_ == 0) - { - processedCommand_.wait(lock); - } - - if (cancel_ && listener_) - { - listener_->SignalCancel(); - } - - // Reset the sequence counters for subsequent commands - bool hasSucceeded = success_; - success_ = true; - totalCommands_ = 0; - cancel_ = false; - - return hasSucceeded; - } - - - void ThreadedCommandProcessor::Cancel() - { - boost::mutex::scoped_lock lock(mutex_); - - cancel_ = true; - } - - - void ThreadedCommandProcessor::SetListener(IListener& listener) - { - boost::mutex::scoped_lock lock(mutex_); - listener_ = &listener; - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/MultiThreading/ThreadedCommandProcessor.h --- a/Core/MultiThreading/ThreadedCommandProcessor.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../ICommand.h" - -#include "SharedMessageQueue.h" - -namespace Orthanc -{ - class ThreadedCommandProcessor - { - public: - class IListener - { - public: - virtual ~IListener() - { - } - - virtual void SignalProgress(unsigned int current, - unsigned int total) = 0; - - virtual void SignalSuccess(unsigned int total) = 0; - - virtual void SignalFailure() = 0; - - virtual void SignalCancel() = 0; - }; - - private: - SharedMessageQueue queue_; - bool done_; - bool cancel_; - std::vector threads_; - IListener* listener_; - - boost::mutex mutex_; - bool success_; - unsigned int remainingCommands_, totalCommands_; - boost::condition_variable processedCommand_; - - static void Processor(ThreadedCommandProcessor* that); - - public: - ThreadedCommandProcessor(unsigned int numThreads); - - ~ThreadedCommandProcessor(); - - // This takes the ownership of the command - void Post(ICommand* command); - - bool Join(); - - void Cancel(); - - void SetListener(IListener& listener); - - IListener& GetListener() const - { - return *listener_; - } - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/OrthancException.cpp --- a/Core/OrthancException.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "PrecompiledHeaders.h" -#include "OrthancException.h" - -namespace Orthanc -{ - const char* OrthancException::What() const - { - if (error_ == ErrorCode_Custom) - { - return custom_.c_str(); - } - else - { - return GetDescription(error_); - } - } - - - const char* OrthancException::GetDescription(ErrorCode error) - { - switch (error) - { - case ErrorCode_Success: - return "Success"; - - case ErrorCode_ParameterOutOfRange: - return "Parameter out of range"; - - case ErrorCode_NotImplemented: - return "Not implemented yet"; - - case ErrorCode_InternalError: - return "Internal error"; - - case ErrorCode_NotEnoughMemory: - return "Not enough memory"; - - case ErrorCode_UriSyntax: - return "Badly formatted URI"; - - case ErrorCode_BadParameterType: - return "Bad type for a parameter"; - - case ErrorCode_InexistentFile: - return "Inexistent file"; - - case ErrorCode_BadFileFormat: - return "Bad file format"; - - case ErrorCode_CannotWriteFile: - return "Cannot write to file"; - - case ErrorCode_Timeout: - return "Timeout"; - - case ErrorCode_UnknownResource: - return "Unknown resource"; - - case ErrorCode_BadSequenceOfCalls: - return "Bad sequence of calls"; - - case ErrorCode_IncompatibleDatabaseVersion: - return "Incompatible version of the database"; - - case ErrorCode_FullStorage: - return "The file storage is full"; - - case ErrorCode_InexistentItem: - return "Accessing an inexistent item"; - - case ErrorCode_BadRequest: - return "Bad request"; - - case ErrorCode_NetworkProtocol: - return "Error in the network protocol"; - - case ErrorCode_CorruptedFile: - return "Corrupted file (inconsistent MD5 hash)"; - - case ErrorCode_InexistentTag: - return "Inexistent tag"; - - case ErrorCode_ReadOnly: - return "Cannot modify a read-only data structure"; - - case ErrorCode_IncompatibleImageSize: - return "Incompatible size of the images"; - - case ErrorCode_IncompatibleImageFormat: - return "Incompatible format of the images"; - - case ErrorCode_SharedLibrary: - return "Error while using a shared library (plugin)"; - - case ErrorCode_SystemCommand: - return "Error while calling a system command"; - - case ErrorCode_Plugin: - return "Error encountered inside a plugin"; - - case ErrorCode_Database: - return "Error with the database engine"; - - case ErrorCode_Custom: - default: - return "???"; - } - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 Core/OrthancException.h --- a/Core/OrthancException.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/OrthancException.h Wed Sep 30 13:23:31 2015 +0200 @@ -32,6 +32,7 @@ #pragma once +#include #include #include "Enumerations.h" @@ -40,33 +41,36 @@ class OrthancException { protected: - ErrorCode error_; - std::string custom_; + ErrorCode errorCode_; + HttpStatus httpStatus_; public: - static const char* GetDescription(ErrorCode error); - - OrthancException(const char* custom) : - error_(ErrorCode_Custom), - custom_(custom) + OrthancException(ErrorCode errorCode) : + errorCode_(errorCode), + httpStatus_(ConvertErrorCodeToHttpStatus(errorCode)) { } - OrthancException(const std::string& custom) : - error_(ErrorCode_Custom), - custom_(custom) - { - } - - OrthancException(ErrorCode error) : error_(error) + OrthancException(ErrorCode errorCode, + HttpStatus httpStatus) : + errorCode_(errorCode), + httpStatus_(httpStatus) { } ErrorCode GetErrorCode() const { - return error_; + return errorCode_; } - const char* What() const; + HttpStatus GetHttpStatus() const + { + return httpStatus_; + } + + const char* What() const + { + return EnumerationToString(errorCode_); + } }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/PrecompiledHeaders.h --- a/Core/PrecompiledHeaders.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/PrecompiledHeaders.h Wed Sep 30 13:23:31 2015 +0200 @@ -46,7 +46,6 @@ #include #include -#include #include #if ORTHANC_PUGIXML_ENABLED == 1 @@ -54,6 +53,7 @@ #endif #include "Enumerations.h" +#include "Logging.h" #include "OrthancException.h" #include "Toolbox.h" #include "Uuid.h" diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApi.cpp --- a/Core/RestApi/RestApi.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApi.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -33,9 +33,9 @@ #include "../PrecompiledHeaders.h" #include "RestApi.h" +#include "../Logging.h" + #include // To define "_exit()" under Windows -#include - #include namespace Orthanc @@ -48,30 +48,42 @@ private: RestApi& api_; RestApiOutput& output_; + RequestOrigin origin_; + const char* remoteIp_; + const char* username_; HttpMethod method_; - const HttpHandler::Arguments& headers_; - const HttpHandler::Arguments& getArguments_; - const std::string& postData_; + const IHttpHandler::Arguments& headers_; + const IHttpHandler::Arguments& getArguments_; + const char* bodyData_; + size_t bodySize_; public: HttpHandlerVisitor(RestApi& api, RestApiOutput& output, + RequestOrigin origin, + const char* remoteIp, + const char* username, HttpMethod method, - const HttpHandler::Arguments& headers, - const HttpHandler::Arguments& getArguments, - const std::string& postData) : + const IHttpHandler::Arguments& headers, + const IHttpHandler::Arguments& getArguments, + const char* bodyData, + size_t bodySize) : api_(api), output_(output), + origin_(origin), + remoteIp_(remoteIp), + username_(username), method_(method), headers_(headers), getArguments_(getArguments), - postData_(postData) + bodyData_(bodyData), + bodySize_(bodySize) { } virtual bool Visit(const RestApiHierarchy::Resource& resource, const UriComponents& uri, - const HttpHandler::Arguments& components, + const IHttpHandler::Arguments& components, const UriComponents& trailing) { if (resource.HasHandler(method_)) @@ -80,28 +92,32 @@ { case HttpMethod_Get: { - RestApiGetCall call(output_, api_, headers_, components, trailing, uri, getArguments_); + RestApiGetCall call(output_, api_, origin_, remoteIp_, username_, + headers_, components, trailing, uri, getArguments_); resource.Handle(call); return true; } case HttpMethod_Post: { - RestApiPostCall call(output_, api_, headers_, components, trailing, uri, postData_); + RestApiPostCall call(output_, api_, origin_, remoteIp_, username_, + headers_, components, trailing, uri, bodyData_, bodySize_); resource.Handle(call); return true; } case HttpMethod_Delete: { - RestApiDeleteCall call(output_, api_, headers_, components, trailing, uri); + RestApiDeleteCall call(output_, api_, origin_, remoteIp_, username_, + headers_, components, trailing, uri); resource.Handle(call); return true; } case HttpMethod_Put: { - RestApiPutCall call(output_, api_, headers_, components, trailing, uri, postData_); + RestApiPutCall call(output_, api_, origin_, remoteIp_, username_, + headers_, components, trailing, uri, bodyData_, bodySize_); resource.Handle(call); return true; } @@ -157,38 +173,48 @@ bool RestApi::Handle(HttpOutput& output, + RequestOrigin origin, + const char* remoteIp, + const char* username, HttpMethod method, const UriComponents& uri, const Arguments& headers, - const Arguments& getArguments, - const std::string& postData) + const GetArguments& getArguments, + const char* bodyData, + size_t bodySize) { - RestApiOutput wrappedOutput(output); + RestApiOutput wrappedOutput(output, method); #if ORTHANC_PUGIXML_ENABLED == 1 - // Look if the user wishes XML answers instead of JSON - // http://www.w3.org/Protocols/HTTP/HTRQ_Headers.html#z3 - Arguments::const_iterator it = headers.find("accept"); - if (it != headers.end()) { - std::vector accepted; - Toolbox::TokenizeString(accepted, it->second, ';'); - for (size_t i = 0; i < accepted.size(); i++) + // Look if the client wishes XML answers instead of JSON + // http://www.w3.org/Protocols/HTTP/HTRQ_Headers.html#z3 + Arguments::const_iterator it = headers.find("accept"); + if (it != headers.end()) { - if (accepted[i] == "application/xml") + std::vector accepted; + Toolbox::TokenizeString(accepted, it->second, ';'); + for (size_t i = 0; i < accepted.size(); i++) { - wrappedOutput.SetConvertJsonToXml(true); - } + if (accepted[i] == "application/xml") + { + wrappedOutput.SetConvertJsonToXml(true); + } - if (accepted[i] == "application/json") - { - wrappedOutput.SetConvertJsonToXml(false); + if (accepted[i] == "application/json") + { + wrappedOutput.SetConvertJsonToXml(false); + } } } } #endif - HttpHandlerVisitor visitor(*this, wrappedOutput, method, headers, getArguments, postData); + Arguments compiled; + HttpToolbox::CompileGetArguments(compiled, getArguments); + + HttpHandlerVisitor visitor(*this, wrappedOutput, origin, remoteIp, username, + method, headers, compiled, bodyData, bodySize); if (root_.LookupResource(uri, visitor)) { diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApi.h --- a/Core/RestApi/RestApi.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApi.h Wed Sep 30 13:23:31 2015 +0200 @@ -38,7 +38,7 @@ namespace Orthanc { - class RestApi : public HttpHandler + class RestApi : public IHttpHandler { private: RestApiHierarchy root_; @@ -47,11 +47,15 @@ static void AutoListChildren(RestApiGetCall& call); virtual bool Handle(HttpOutput& output, + RequestOrigin origin, + const char* remoteIp, + const char* username, HttpMethod method, const UriComponents& uri, const Arguments& headers, - const Arguments& getArguments, - const std::string& postData); + const GetArguments& getArguments, + const char* bodyData, + size_t bodySize); void Register(const std::string& path, RestApiGetCall::Handler handler); diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiCall.cpp --- a/Core/RestApi/RestApiCall.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiCall.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -30,6 +30,7 @@ **/ +#include "../PrecompiledHeaders.h" #include "RestApiCall.h" namespace Orthanc @@ -41,4 +42,17 @@ Json::Reader reader; return reader.parse(request, result); } + + + std::string RestApiCall::FlattenUri() const + { + std::string s = "/"; + + for (size_t i = 0; i < fullUri_.size(); i++) + { + s += fullUri_[i] + "/"; + } + + return s; + } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiCall.h --- a/Core/RestApi/RestApiCall.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiCall.h Wed Sep 30 13:23:31 2015 +0200 @@ -32,7 +32,8 @@ #pragma once -#include "../HttpServer/HttpHandler.h" +#include "../HttpServer/IHttpHandler.h" +#include "../HttpServer/HttpToolbox.h" #include "RestApiPath.h" #include "RestApiOutput.h" @@ -47,8 +48,11 @@ private: RestApiOutput& output_; RestApi& context_; - const HttpHandler::Arguments& httpHeaders_; - const HttpHandler::Arguments& uriComponents_; + RequestOrigin origin_; + const char* remoteIp_; + const char* username_; + const IHttpHandler::Arguments& httpHeaders_; + const IHttpHandler::Arguments& uriComponents_; const UriComponents& trailing_; const UriComponents& fullUri_; @@ -59,12 +63,18 @@ public: RestApiCall(RestApiOutput& output, RestApi& context, - const HttpHandler::Arguments& httpHeaders, - const HttpHandler::Arguments& uriComponents, + RequestOrigin origin, + const char* remoteIp, + const char* username, + const IHttpHandler::Arguments& httpHeaders, + const IHttpHandler::Arguments& uriComponents, const UriComponents& trailing, const UriComponents& fullUri) : output_(output), context_(context), + origin_(origin), + remoteIp_(remoteIp), + username_(username), httpHeaders_(httpHeaders), uriComponents_(uriComponents), trailing_(trailing), @@ -95,23 +105,40 @@ std::string GetUriComponent(const std::string& name, const std::string& defaultValue) const { - return HttpHandler::GetArgument(uriComponents_, name, defaultValue); + return HttpToolbox::GetArgument(uriComponents_, name, defaultValue); } std::string GetHttpHeader(const std::string& name, const std::string& defaultValue) const { - return HttpHandler::GetArgument(httpHeaders_, name, defaultValue); + return HttpToolbox::GetArgument(httpHeaders_, name, defaultValue); } - const HttpHandler::Arguments& GetHttpHeaders() const + const IHttpHandler::Arguments& GetHttpHeaders() const { return httpHeaders_; } - void ParseCookies(HttpHandler::Arguments& result) const + void ParseCookies(IHttpHandler::Arguments& result) const + { + HttpToolbox::ParseCookies(result, httpHeaders_); + } + + std::string FlattenUri() const; + + RequestOrigin GetRequestOrigin() const { - HttpHandler::ParseCookies(result, httpHeaders_); + return origin_; + } + + const char* GetRemoteIp() const + { + return remoteIp_; + } + + const char* GetUsername() const + { + return username_; } virtual bool ParseJsonRequest(Json::Value& result) const = 0; diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiDeleteCall.h --- a/Core/RestApi/RestApiDeleteCall.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiDeleteCall.h Wed Sep 30 13:23:31 2015 +0200 @@ -43,11 +43,15 @@ RestApiDeleteCall(RestApiOutput& output, RestApi& context, - const HttpHandler::Arguments& httpHeaders, - const HttpHandler::Arguments& uriComponents, + RequestOrigin origin, + const char* remoteIp, + const char* username, + const IHttpHandler::Arguments& httpHeaders, + const IHttpHandler::Arguments& uriComponents, const UriComponents& trailing, const UriComponents& fullUri) : - RestApiCall(output, context, httpHeaders, uriComponents, trailing, fullUri) + RestApiCall(output, context, origin, remoteIp, username, + httpHeaders, uriComponents, trailing, fullUri) { } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiGetCall.cpp --- a/Core/RestApi/RestApiGetCall.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiGetCall.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -30,6 +30,7 @@ **/ +#include "../PrecompiledHeaders.h" #include "RestApiGetCall.h" namespace Orthanc @@ -38,7 +39,7 @@ { result.clear(); - for (HttpHandler::Arguments::const_iterator + for (IHttpHandler::Arguments::const_iterator it = getArguments_.begin(); it != getArguments_.end(); ++it) { result[it->first] = it->second; diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiGetCall.h --- a/Core/RestApi/RestApiGetCall.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiGetCall.h Wed Sep 30 13:23:31 2015 +0200 @@ -39,19 +39,23 @@ class RestApiGetCall : public RestApiCall { private: - const HttpHandler::Arguments& getArguments_; + const IHttpHandler::Arguments& getArguments_; public: typedef void (*Handler) (RestApiGetCall& call); RestApiGetCall(RestApiOutput& output, RestApi& context, - const HttpHandler::Arguments& httpHeaders, - const HttpHandler::Arguments& uriComponents, + RequestOrigin origin, + const char* remoteIp, + const char* username, + const IHttpHandler::Arguments& httpHeaders, + const IHttpHandler::Arguments& uriComponents, const UriComponents& trailing, const UriComponents& fullUri, - const HttpHandler::Arguments& getArguments) : - RestApiCall(output, context, httpHeaders, uriComponents, trailing, fullUri), + const IHttpHandler::Arguments& getArguments) : + RestApiCall(output, context, origin, remoteIp, username, + httpHeaders, uriComponents, trailing, fullUri), getArguments_(getArguments) { } @@ -59,7 +63,7 @@ std::string GetArgument(const std::string& name, const std::string& defaultValue) const { - return HttpHandler::GetArgument(getArguments_, name, defaultValue); + return HttpToolbox::GetArgument(getArguments_, name, defaultValue); } bool HasArgument(const std::string& name) const diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiHierarchy.cpp --- a/Core/RestApi/RestApiHierarchy.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiHierarchy.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -30,6 +30,7 @@ **/ +#include "../PrecompiledHeaders.h" #include "RestApiHierarchy.h" #include "../OrthancException.h" @@ -199,7 +200,7 @@ } - bool RestApiHierarchy::LookupResource(HttpHandler::Arguments& components, + bool RestApiHierarchy::LookupResource(IHttpHandler::Arguments& components, const UriComponents& uri, IVisitor& visitor, size_t level) @@ -240,7 +241,7 @@ for (child = wildcardChildren_.begin(); child != wildcardChildren_.end(); ++child) { - HttpHandler::Arguments subComponents = components; + IHttpHandler::Arguments subComponents = components; subComponents[child->first] = uri[level]; if (child->second->LookupResource(subComponents, uri, visitor, level + 1)) @@ -404,7 +405,7 @@ bool RestApiHierarchy::LookupResource(const UriComponents& uri, IVisitor& visitor) { - HttpHandler::Arguments components; + IHttpHandler::Arguments components; return LookupResource(components, uri, visitor, 0); } @@ -426,7 +427,7 @@ virtual bool Visit(const RestApiHierarchy::Resource& resource, const UriComponents& uri, - const HttpHandler::Arguments& components, + const IHttpHandler::Arguments& components, const UriComponents& trailing) { if (trailing.size() == 0) // Ignore universal handlers @@ -460,14 +461,15 @@ void RestApiHierarchy::GetAcceptedMethods(std::set& methods, const UriComponents& uri) { - HttpHandler::Arguments components; + IHttpHandler::Arguments components; AcceptedMethodsVisitor visitor(methods); - LookupResource(components, uri, visitor, 0); - - Json::Value d; - if (GetDirectory(d, uri)) + if (LookupResource(components, uri, visitor, 0)) { - methods.insert(HttpMethod_Get); + Json::Value d; + if (GetDirectory(d, uri)) + { + methods.insert(HttpMethod_Get); + } } } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiHierarchy.h --- a/Core/RestApi/RestApiHierarchy.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiHierarchy.h Wed Sep 30 13:23:31 2015 +0200 @@ -98,7 +98,7 @@ virtual bool Visit(const Resource& resource, const UriComponents& uri, - const HttpHandler::Arguments& components, + const IHttpHandler::Arguments& components, const UriComponents& trailing) = 0; }; @@ -123,7 +123,7 @@ bool CanGenerateDirectory() const; - bool LookupResource(HttpHandler::Arguments& components, + bool LookupResource(IHttpHandler::Arguments& components, const UriComponents& uri, IVisitor& visitor, size_t level); diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiOutput.cpp --- a/Core/RestApi/RestApiOutput.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiOutput.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -33,15 +33,18 @@ #include "../PrecompiledHeaders.h" #include "RestApiOutput.h" +#include "../Logging.h" +#include "../OrthancException.h" + #include -#include -#include "../OrthancException.h" namespace Orthanc { - RestApiOutput::RestApiOutput(HttpOutput& output) : + RestApiOutput::RestApiOutput(HttpOutput& output, + HttpMethod method) : output_(output), + method_(method), convertJsonToXml_(false) { alreadySent_ = false; @@ -55,7 +58,14 @@ { if (!alreadySent_) { - output_.SendStatus(HttpStatus_404_NotFound); + if (method_ == HttpMethod_Post) + { + output_.SendStatus(HttpStatus_400_BadRequest); + } + else + { + output_.SendStatus(HttpStatus_404_NotFound); + } } } @@ -67,10 +77,11 @@ } } - void RestApiOutput::AnswerFile(HttpFileSender& sender) + + void RestApiOutput::AnswerStream(IHttpStreamAnswer& stream) { CheckStatus(); - sender.Send(output_); + output_.Answer(stream); alreadySent_ = true; } @@ -84,7 +95,7 @@ std::string s; Toolbox::JsonToXml(s, value); output_.SetContentType("application/xml"); - output_.SendBody(s); + output_.Answer(s); #else LOG(ERROR) << "Orthanc was compiled without XML support"; throw OrthancException(ErrorCode_InternalError); @@ -94,7 +105,7 @@ { Json::StyledWriter writer; output_.SetContentType("application/json"); - output_.SendBody(writer.write(value)); + output_.Answer(writer.write(value)); } alreadySent_ = true; @@ -103,10 +114,8 @@ void RestApiOutput::AnswerBuffer(const std::string& buffer, const std::string& contentType) { - CheckStatus(); - output_.SetContentType(contentType.c_str()); - output_.SendBody(buffer); - alreadySent_ = true; + AnswerBuffer(buffer.size() == 0 ? NULL : buffer.c_str(), + buffer.size(), contentType); } void RestApiOutput::AnswerBuffer(const void* buffer, @@ -115,7 +124,7 @@ { CheckStatus(); output_.SetContentType(contentType.c_str()); - output_.SendBody(buffer, length); + output_.Answer(buffer, length); alreadySent_ = true; } @@ -126,21 +135,34 @@ alreadySent_ = true; } - void RestApiOutput::SignalError(HttpStatus status) + void RestApiOutput::SignalErrorInternal(HttpStatus status, + const char* message, + size_t messageSize) { if (status != HttpStatus_400_BadRequest && status != HttpStatus_403_Forbidden && status != HttpStatus_500_InternalServerError && status != HttpStatus_415_UnsupportedMediaType) { - throw OrthancException("This HTTP status is not allowed in a REST API"); + throw OrthancException(ErrorCode_BadHttpStatusInRest); } CheckStatus(); - output_.SendStatus(status); + output_.SendStatus(status, message, messageSize); alreadySent_ = true; } + void RestApiOutput::SignalError(HttpStatus status) + { + SignalErrorInternal(status, NULL, 0); + } + + void RestApiOutput::SignalError(HttpStatus status, + const std::string& message) + { + SignalErrorInternal(status, message.c_str(), message.size()); + } + void RestApiOutput::SetCookie(const std::string& name, const std::string& value, unsigned int maxAge) diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiOutput.h --- a/Core/RestApi/RestApiOutput.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiOutput.h Wed Sep 30 13:23:31 2015 +0200 @@ -42,27 +42,23 @@ class RestApiOutput { private: - HttpOutput& output_; - bool alreadySent_; - bool convertJsonToXml_; + HttpOutput& output_; + HttpMethod method_; + bool alreadySent_; + bool convertJsonToXml_; void CheckStatus(); + void SignalErrorInternal(HttpStatus status, + const char* message, + size_t messageSize); + public: - RestApiOutput(HttpOutput& output); + RestApiOutput(HttpOutput& output, + HttpMethod method); ~RestApiOutput(); - HttpOutput& GetLowLevelOutput() - { - return output_; - } - - void MarkLowLevelOutputDone() - { - alreadySent_ = true; - } - void SetConvertJsonToXml(bool convert) { convertJsonToXml_ = convert; @@ -73,7 +69,7 @@ return convertJsonToXml_; } - void AnswerFile(HttpFileSender& sender); + void AnswerStream(IHttpStreamAnswer& stream); void AnswerJson(const Json::Value& value); @@ -86,6 +82,9 @@ void SignalError(HttpStatus status); + void SignalError(HttpStatus status, + const std::string& message); + void Redirect(const std::string& path); void SetCookie(const std::string& name, diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiPath.cpp --- a/Core/RestApi/RestApiPath.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiPath.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -78,7 +78,7 @@ } } - bool RestApiPath::Match(HttpHandler::Arguments& components, + bool RestApiPath::Match(IHttpHandler::Arguments& components, UriComponents& trailing, const std::string& uriRaw) const { @@ -87,7 +87,7 @@ return Match(components, trailing, uri); } - bool RestApiPath::Match(HttpHandler::Arguments& components, + bool RestApiPath::Match(IHttpHandler::Arguments& components, UriComponents& trailing, const UriComponents& uri) const { @@ -135,7 +135,7 @@ bool RestApiPath::Match(const UriComponents& uri) const { - HttpHandler::Arguments components; + IHttpHandler::Arguments components; UriComponents trailing; return Match(components, trailing, uri); } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiPath.h --- a/Core/RestApi/RestApiPath.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiPath.h Wed Sep 30 13:23:31 2015 +0200 @@ -33,7 +33,7 @@ #pragma once #include "../Toolbox.h" -#include "../HttpServer/HttpHandler.h" +#include "../HttpServer/IHttpHandler.h" #include @@ -50,11 +50,11 @@ RestApiPath(const std::string& uri); // This version is slower - bool Match(HttpHandler::Arguments& components, + bool Match(IHttpHandler::Arguments& components, UriComponents& trailing, const std::string& uriRaw) const; - bool Match(HttpHandler::Arguments& components, + bool Match(IHttpHandler::Arguments& components, UriComponents& trailing, const UriComponents& uri) const; diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiPostCall.h --- a/Core/RestApi/RestApiPostCall.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiPostCall.h Wed Sep 30 13:23:31 2015 +0200 @@ -39,31 +39,48 @@ class RestApiPostCall : public RestApiCall { private: - const std::string& data_; + const char* bodyData_; + size_t bodySize_; public: typedef void (*Handler) (RestApiPostCall& call); RestApiPostCall(RestApiOutput& output, RestApi& context, - const HttpHandler::Arguments& httpHeaders, - const HttpHandler::Arguments& uriComponents, + RequestOrigin origin, + const char* remoteIp, + const char* username, + const IHttpHandler::Arguments& httpHeaders, + const IHttpHandler::Arguments& uriComponents, const UriComponents& trailing, const UriComponents& fullUri, - const std::string& data) : - RestApiCall(output, context, httpHeaders, uriComponents, trailing, fullUri), - data_(data) + const char* bodyData, + size_t bodySize) : + RestApiCall(output, context, origin, remoteIp, username, + httpHeaders, uriComponents, trailing, fullUri), + bodyData_(bodyData), + bodySize_(bodySize) { } - const std::string& GetPostBody() const + const char* GetBodyData() const + { + return bodyData_; + } + + size_t GetBodySize() const { - return data_; + return bodySize_; + } + + void BodyToString(std::string& result) const + { + result.assign(bodyData_, bodySize_); } virtual bool ParseJsonRequest(Json::Value& result) const { - return ParseJsonRequestInternal(result, GetPostBody().c_str()); + return ParseJsonRequestInternal(result, bodyData_); } }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/RestApi/RestApiPutCall.h --- a/Core/RestApi/RestApiPutCall.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/RestApi/RestApiPutCall.h Wed Sep 30 13:23:31 2015 +0200 @@ -39,31 +39,48 @@ class RestApiPutCall : public RestApiCall { private: - const std::string& data_; + const char* bodyData_; + size_t bodySize_; public: typedef void (*Handler) (RestApiPutCall& call); RestApiPutCall(RestApiOutput& output, RestApi& context, - const HttpHandler::Arguments& httpHeaders, - const HttpHandler::Arguments& uriComponents, + RequestOrigin origin, + const char* remoteIp, + const char* username, + const IHttpHandler::Arguments& httpHeaders, + const IHttpHandler::Arguments& uriComponents, const UriComponents& trailing, const UriComponents& fullUri, - const std::string& data) : - RestApiCall(output, context, httpHeaders, uriComponents, trailing, fullUri), - data_(data) + const char* bodyData, + size_t bodySize) : + RestApiCall(output, context, origin, remoteIp, username, + httpHeaders, uriComponents, trailing, fullUri), + bodyData_(bodyData), + bodySize_(bodySize) { } - const std::string& GetPutBody() const + const char* GetBodyData() const + { + return bodyData_; + } + + size_t GetBodySize() const { - return data_; + return bodySize_; + } + + void BodyToString(std::string& result) const + { + result.assign(bodyData_, bodySize_); } virtual bool ParseJsonRequest(Json::Value& result) const { - return ParseJsonRequestInternal(result, GetPutBody().c_str()); + return ParseJsonRequestInternal(result, bodyData_); } }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/SQLite/Connection.cpp --- a/Core/SQLite/Connection.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/SQLite/Connection.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -44,13 +44,14 @@ #include #include -#include #include #if ORTHANC_SQLITE_STANDALONE != 1 -#include +#include "../Logging.h" #endif +#include "sqlite3.h" + namespace Orthanc { @@ -74,7 +75,7 @@ { if (!db_) { - throw OrthancSQLiteException("SQLite: The database is not opened"); + throw OrthancSQLiteException(ErrorCode_SQLiteNotOpened); } } @@ -82,7 +83,7 @@ { if (db_) { - throw OrthancSQLiteException("SQLite: Connection is already open"); + throw OrthancSQLiteException(ErrorCode_SQLiteAlreadyOpened); } int err = sqlite3_open(path.c_str(), &db_); @@ -90,7 +91,7 @@ { Close(); db_ = NULL; - throw OrthancSQLiteException("SQLite: Unable to open the database"); + throw OrthancSQLiteException(ErrorCode_SQLiteCannotOpen); } // Execute PRAGMAs at this point @@ -136,7 +137,7 @@ { if (i->second->GetReferenceCount() >= 1) { - throw OrthancSQLiteException("SQLite: This cached statement is already being referred to"); + throw OrthancSQLiteException(ErrorCode_SQLiteStatementAlreadyUsed); } return *i->second; @@ -161,7 +162,11 @@ int error = sqlite3_exec(db_, sql, NULL, NULL, NULL); if (error == SQLITE_ERROR) { - throw OrthancSQLiteException("SQLite Execute error: " + std::string(sqlite3_errmsg(db_))); +#if ORTHANC_SQLITE_STANDALONE != 1 + LOG(ERROR) << "SQLite execute error: " << sqlite3_errmsg(db_); +#endif + + throw OrthancSQLiteException(ErrorCode_SQLiteExecute); } else { @@ -282,7 +287,7 @@ { if (!transactionNesting_) { - throw OrthancSQLiteException("Rolling back a nonexistent transaction"); + throw OrthancSQLiteException(ErrorCode_SQLiteRollbackWithoutTransaction); } transactionNesting_--; @@ -301,7 +306,7 @@ { if (!transactionNesting_) { - throw OrthancSQLiteException("Committing a nonexistent transaction"); + throw OrthancSQLiteException(ErrorCode_SQLiteCommitWithoutTransaction); } transactionNesting_--; @@ -369,7 +374,7 @@ if (err != SQLITE_OK) { delete func; - throw OrthancSQLiteException("SQLite: Unable to register a function"); + throw OrthancSQLiteException(ErrorCode_SQLiteRegisterFunction); } return func; @@ -386,7 +391,7 @@ if (err != SQLITE_OK) { - throw OrthancSQLiteException("SQLite: Unable to flush the database"); + throw OrthancSQLiteException(ErrorCode_SQLiteFlush); } } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/SQLite/FunctionContext.cpp --- a/Core/SQLite/FunctionContext.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/SQLite/FunctionContext.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -39,7 +39,9 @@ #include "FunctionContext.h" #include "OrthancSQLiteException.h" -#include +#include + +#include "sqlite3.h" namespace Orthanc { @@ -62,7 +64,7 @@ { if (index >= argc_) { - throw OrthancSQLiteException("Parameter out of range"); + throw OrthancSQLiteException(ErrorCode_ParameterOutOfRange); } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/SQLite/OrthancSQLiteException.h --- a/Core/SQLite/OrthancSQLiteException.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/SQLite/OrthancSQLiteException.h Wed Sep 30 13:23:31 2015 +0200 @@ -45,17 +45,99 @@ { namespace SQLite { + // Auto-generated by "Resources/GenerateErrorCodes.py" + enum ErrorCode + { + ErrorCode_ParameterOutOfRange, + ErrorCode_BadParameterType, + ErrorCode_SQLiteNotOpened, + ErrorCode_SQLiteAlreadyOpened, + ErrorCode_SQLiteCannotOpen, + ErrorCode_SQLiteStatementAlreadyUsed, + ErrorCode_SQLiteExecute, + ErrorCode_SQLiteRollbackWithoutTransaction, + ErrorCode_SQLiteCommitWithoutTransaction, + ErrorCode_SQLiteRegisterFunction, + ErrorCode_SQLiteFlush, + ErrorCode_SQLiteCannotRun, + ErrorCode_SQLiteCannotStep, + ErrorCode_SQLiteBindOutOfRange, + ErrorCode_SQLitePrepareStatement, + ErrorCode_SQLiteTransactionAlreadyStarted, + ErrorCode_SQLiteTransactionCommit, + ErrorCode_SQLiteTransactionBegin + }; + class OrthancSQLiteException : public ::std::runtime_error { public: - OrthancSQLiteException(const std::string& what) : - ::std::runtime_error(what) + OrthancSQLiteException(ErrorCode error) : + ::std::runtime_error(EnumerationToString(error)) { } - OrthancSQLiteException(const char* what) : - ::std::runtime_error(what) + // Auto-generated by "Resources/GenerateErrorCodes.py" + static const char* EnumerationToString(ErrorCode code) { + switch (code) + { + case ErrorCode_ParameterOutOfRange: + return "Parameter out of range"; + + case ErrorCode_BadParameterType: + return "Bad type for a parameter"; + + case ErrorCode_SQLiteNotOpened: + return "SQLite: The database is not opened"; + + case ErrorCode_SQLiteAlreadyOpened: + return "SQLite: Connection is already open"; + + case ErrorCode_SQLiteCannotOpen: + return "SQLite: Unable to open the database"; + + case ErrorCode_SQLiteStatementAlreadyUsed: + return "SQLite: This cached statement is already being referred to"; + + case ErrorCode_SQLiteExecute: + return "SQLite: Cannot execute a command"; + + case ErrorCode_SQLiteRollbackWithoutTransaction: + return "SQLite: Rolling back a nonexistent transaction (have you called Begin()?)"; + + case ErrorCode_SQLiteCommitWithoutTransaction: + return "SQLite: Committing a nonexistent transaction"; + + case ErrorCode_SQLiteRegisterFunction: + return "SQLite: Unable to register a function"; + + case ErrorCode_SQLiteFlush: + return "SQLite: Unable to flush the database"; + + case ErrorCode_SQLiteCannotRun: + return "SQLite: Cannot run a cached statement"; + + case ErrorCode_SQLiteCannotStep: + return "SQLite: Cannot step over a cached statement"; + + case ErrorCode_SQLiteBindOutOfRange: + return "SQLite: Bing a value while out of range (serious error)"; + + case ErrorCode_SQLitePrepareStatement: + return "SQLite: Cannot prepare a cached statement"; + + case ErrorCode_SQLiteTransactionAlreadyStarted: + return "SQLite: Beginning the same transaction twice"; + + case ErrorCode_SQLiteTransactionCommit: + return "SQLite: Failure when committing the transaction"; + + case ErrorCode_SQLiteTransactionBegin: + return "SQLite: Cannot start a transaction"; + + default: + return "Unknown error code"; + } } }; } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/SQLite/Statement.cpp --- a/Core/SQLite/Statement.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/SQLite/Statement.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -42,15 +42,16 @@ #include "Statement.h" #include "Connection.h" -#include #include #include #include #if ORTHANC_SQLITE_STANDALONE != 1 -#include +#include "../Logging.h" #endif +#include "sqlite3.h" + #if defined(_MSC_VER) #define snprintf _snprintf #endif @@ -59,31 +60,39 @@ { namespace SQLite { - int Statement::CheckError(int err) const + int Statement::CheckError(int err, ErrorCode code) const { bool succeeded = (err == SQLITE_OK || err == SQLITE_ROW || err == SQLITE_DONE); if (!succeeded) { +#if ORTHANC_SQLITE_STANDALONE != 1 char buffer[128]; snprintf(buffer, sizeof(buffer) - 1, "SQLite error code %d", err); - throw OrthancSQLiteException(buffer); + LOG(ERROR) << buffer; +#endif + + throw OrthancSQLiteException(code); } return err; } - void Statement::CheckOk(int err) const + void Statement::CheckOk(int err, ErrorCode code) const { if (err == SQLITE_RANGE) { // Binding to a non-existent variable is evidence of a serious error. - throw OrthancSQLiteException("Bind value out of range"); + throw OrthancSQLiteException(ErrorCode_SQLiteBindOutOfRange); } else if (err != SQLITE_OK) { +#if ORTHANC_SQLITE_STANDALONE != 1 char buffer[128]; snprintf(buffer, sizeof(buffer) - 1, "SQLite error code %d", err); - throw OrthancSQLiteException(buffer); + LOG(ERROR) << buffer; +#endif + + throw OrthancSQLiteException(code); } } @@ -126,7 +135,7 @@ VLOG(1) << "SQLite::Statement::Run " << sqlite3_sql(GetStatement()); #endif - return CheckError(sqlite3_step(GetStatement())) == SQLITE_DONE; + return CheckError(sqlite3_step(GetStatement()), ErrorCode_SQLiteCannotRun) == SQLITE_DONE; } bool Statement::Step() @@ -135,7 +144,7 @@ VLOG(1) << "SQLite::Statement::Step " << sqlite3_sql(GetStatement()); #endif - return CheckError(sqlite3_step(GetStatement())) == SQLITE_ROW; + return CheckError(sqlite3_step(GetStatement()), ErrorCode_SQLiteCannotStep) == SQLITE_ROW; } void Statement::Reset(bool clear_bound_vars) @@ -157,7 +166,8 @@ void Statement::BindNull(int col) { - CheckOk(sqlite3_bind_null(GetStatement(), col + 1)); + CheckOk(sqlite3_bind_null(GetStatement(), col + 1), + ErrorCode_BadParameterType); } void Statement::BindBool(int col, bool val) @@ -167,22 +177,26 @@ void Statement::BindInt(int col, int val) { - CheckOk(sqlite3_bind_int(GetStatement(), col + 1, val)); + CheckOk(sqlite3_bind_int(GetStatement(), col + 1, val), + ErrorCode_BadParameterType); } void Statement::BindInt64(int col, int64_t val) { - CheckOk(sqlite3_bind_int64(GetStatement(), col + 1, val)); + CheckOk(sqlite3_bind_int64(GetStatement(), col + 1, val), + ErrorCode_BadParameterType); } void Statement::BindDouble(int col, double val) { - CheckOk(sqlite3_bind_double(GetStatement(), col + 1, val)); + CheckOk(sqlite3_bind_double(GetStatement(), col + 1, val), + ErrorCode_BadParameterType); } void Statement::BindCString(int col, const char* val) { - CheckOk(sqlite3_bind_text(GetStatement(), col + 1, val, -1, SQLITE_TRANSIENT)); + CheckOk(sqlite3_bind_text(GetStatement(), col + 1, val, -1, SQLITE_TRANSIENT), + ErrorCode_BadParameterType); } void Statement::BindString(int col, const std::string& val) @@ -191,7 +205,8 @@ col + 1, val.data(), val.size(), - SQLITE_TRANSIENT)); + SQLITE_TRANSIENT), + ErrorCode_BadParameterType); } /*void Statement::BindString16(int col, const string16& value) @@ -201,7 +216,8 @@ void Statement::BindBlob(int col, const void* val, int val_len) { - CheckOk(sqlite3_bind_blob(GetStatement(), col + 1, val, val_len, SQLITE_TRANSIENT)); + CheckOk(sqlite3_bind_blob(GetStatement(), col + 1, val, val_len, SQLITE_TRANSIENT), + ErrorCode_BadParameterType); } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/SQLite/Statement.h --- a/Core/SQLite/Statement.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/SQLite/Statement.h Wed Sep 30 13:23:31 2015 +0200 @@ -81,9 +81,11 @@ private: StatementReference reference_; - int CheckError(int err) const; + int CheckError(int err, + ErrorCode code) const; - void CheckOk(int err) const; + void CheckOk(int err, + ErrorCode code) const; struct sqlite3_stmt* GetStatement() const { diff -r 61ce8147f30d -r 4a5c79e31b60 Core/SQLite/StatementReference.cpp --- a/Core/SQLite/StatementReference.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/SQLite/StatementReference.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -43,9 +43,10 @@ #include "OrthancSQLiteException.h" #if ORTHANC_SQLITE_STANDALONE != 1 -#include +#include "../Logging.h" #endif +#include #include #include "sqlite3.h" @@ -71,7 +72,7 @@ { if (database == NULL || sql == NULL) { - throw OrthancSQLiteException("Parameter out of range"); + throw OrthancSQLiteException(ErrorCode_ParameterOutOfRange); } root_ = NULL; @@ -80,7 +81,11 @@ int error = sqlite3_prepare_v2(database, sql, -1, &statement_, NULL); if (error != SQLITE_OK) { - throw OrthancSQLiteException("SQLite: " + std::string(sqlite3_errmsg(database))); +#if ORTHANC_SQLITE_STANDALONE != 1 + LOG(ERROR) << "SQLite: " << sqlite3_errmsg(database); +#endif + + throw OrthancSQLiteException(ErrorCode_SQLitePrepareStatement); } assert(IsRoot()); diff -r 61ce8147f30d -r 4a5c79e31b60 Core/SQLite/Transaction.cpp --- a/Core/SQLite/Transaction.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/SQLite/Transaction.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -64,13 +64,13 @@ { if (isOpen_) { - throw OrthancSQLiteException("SQLite: Beginning a transaction twice!"); + throw OrthancSQLiteException(ErrorCode_SQLiteTransactionAlreadyStarted); } isOpen_ = connection_.BeginTransaction(); if (!isOpen_) { - throw OrthancSQLiteException("SQLite: Unable to create a transaction"); + throw OrthancSQLiteException(ErrorCode_SQLiteTransactionBegin); } } @@ -78,8 +78,7 @@ { if (!isOpen_) { - throw OrthancSQLiteException("SQLite: Attempting to roll back a nonexistent transaction. " - "Did you remember to call Begin()?"); + throw OrthancSQLiteException(ErrorCode_SQLiteRollbackWithoutTransaction); } isOpen_ = false; @@ -91,15 +90,14 @@ { if (!isOpen_) { - throw OrthancSQLiteException("SQLite: Attempting to roll back a nonexistent transaction. " - "Did you remember to call Begin()?"); + throw OrthancSQLiteException(ErrorCode_SQLiteRollbackWithoutTransaction); } isOpen_ = false; if (!connection_.CommitTransaction()) { - throw OrthancSQLiteException("SQLite: Failure when committing the transaction"); + throw OrthancSQLiteException(ErrorCode_SQLiteTransactionCommit); } } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Toolbox.cpp --- a/Core/Toolbox.cpp Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Toolbox.cpp Wed Sep 30 13:23:31 2015 +0200 @@ -34,21 +34,29 @@ #include "Toolbox.h" #include "OrthancException.h" +#include "Logging.h" +#include #include #include #include #include -#include #include +#include #include #include + +#if BOOST_HAS_DATE_TIME == 1 +#include +#endif + +#if BOOST_HAS_REGEX == 1 #include -#include +#endif #if defined(_WIN32) #include -#include // For "_spawnvp()" +#include // For "_spawnvp()" and "_getpid()" #else #include // For "execvp()" #include // For "waitpid()" @@ -59,7 +67,7 @@ #include /* PATH_MAX */ #endif -#if defined(__linux) || defined(__FreeBSD_kernel__) +#if defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) #include /* PATH_MAX */ #include #include @@ -71,8 +79,15 @@ #include + +#if !defined(ORTHANC_ENABLE_MD5) || ORTHANC_ENABLE_MD5 == 1 #include "../Resources/ThirdParty/md5/md5.h" +#endif + + +#if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1 #include "../Resources/ThirdParty/base64/base64.h" +#endif #if defined(_MSC_VER) && (_MSC_VER < 1800) @@ -116,7 +131,7 @@ { #if defined(_WIN32) ::Sleep(static_cast(microSeconds / static_cast(1000))); -#elif defined(__linux) || defined(__APPLE__) || defined(__FreeBSD_kernel__) +#elif defined(__linux) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) usleep(microSeconds); #else #error Support your platform here @@ -193,6 +208,12 @@ void Toolbox::ReadFile(std::string& content, const std::string& path) { + if (!boost::filesystem::is_regular_file(path)) + { + LOG(ERROR) << "The path does not point to a regular file: " << path; + throw OrthancException(ErrorCode_RegularFileExpected); + } + boost::filesystem::ifstream f; f.open(path, std::ifstream::in | std::ifstream::binary); if (!f.good()) @@ -215,7 +236,8 @@ } - void Toolbox::WriteFile(const std::string& content, + void Toolbox::WriteFile(const void* content, + size_t size, const std::string& path) { boost::filesystem::ofstream f; @@ -225,24 +247,35 @@ throw OrthancException(ErrorCode_CannotWriteFile); } - if (content.size() != 0) + if (size != 0) { - f.write(content.c_str(), content.size()); + f.write(reinterpret_cast(content), size); } f.close(); } + void Toolbox::WriteFile(const std::string& content, + const std::string& path) + { + WriteFile(content.size() > 0 ? content.c_str() : NULL, + content.size(), path); + } + void Toolbox::RemoveFile(const std::string& path) { if (boost::filesystem::exists(path)) { if (boost::filesystem::is_regular_file(path)) + { boost::filesystem::remove(path); + } else - throw OrthancException("The path is not a regular file: " + path); + { + throw OrthancException(ErrorCode_RegularFileExpected); + } } } @@ -422,21 +455,26 @@ { return static_cast(boost::filesystem::file_size(path)); } - catch (boost::filesystem::filesystem_error) + catch (boost::filesystem::filesystem_error&) { throw OrthancException(ErrorCode_InexistentFile); } } +#if !defined(ORTHANC_ENABLE_MD5) || ORTHANC_ENABLE_MD5 == 1 static char GetHexadecimalCharacter(uint8_t value) { assert(value < 16); if (value < 10) + { return value + '0'; + } else + { return (value - 10) + 'a'; + } } @@ -474,12 +512,14 @@ result.resize(32); for (unsigned int i = 0; i < 16; i++) { - result[2 * i] = GetHexadecimalCharacter(actualHash[i] / 16); - result[2 * i + 1] = GetHexadecimalCharacter(actualHash[i] % 16); + result[2 * i] = GetHexadecimalCharacter(static_cast(actualHash[i] / 16)); + result[2 * i + 1] = GetHexadecimalCharacter(static_cast(actualHash[i] % 16)); } } +#endif +#if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1 void Toolbox::EncodeBase64(std::string& result, const std::string& data) { @@ -493,6 +533,31 @@ } +# if BOOST_HAS_REGEX == 1 + void Toolbox::DecodeDataUriScheme(std::string& mime, + std::string& content, + const std::string& source) + { + boost::regex pattern("data:([^;]+);base64,([a-zA-Z0-9=+/]*)", + boost::regex::icase /* case insensitive search */); + + boost::cmatch what; + if (regex_match(source.c_str(), what, pattern)) + { + mime = what[1]; + DecodeBase64(content, what[2]); + } + else + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } +# endif + +#endif + + + #if defined(_WIN32) static std::string GetPathToExecutableInternal() { @@ -503,14 +568,14 @@ return std::string(&buffer[0]); } -#elif defined(__linux) || defined(__FreeBSD_kernel__) +#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) static std::string GetPathToExecutableInternal() { std::vector buffer(PATH_MAX + 1); ssize_t bytes = readlink("/proc/self/exe", &buffer[0], buffer.size() - 1); if (bytes == 0) { - throw OrthancException("Unable to get the path to the executable"); + throw OrthancException(ErrorCode_PathToExecutable); } return std::string(&buffer[0]); @@ -546,73 +611,89 @@ } - std::string Toolbox::ConvertToUtf8(const std::string& source, - const Encoding sourceEncoding) + static const char* GetBoostLocaleEncoding(const Encoding sourceEncoding) { - const char* encoding; - - - // http://bradleyross.users.sourceforge.net/docs/dicom/doc/src-html/org/dcm4che2/data/SpecificCharacterSet.html switch (sourceEncoding) { case Encoding_Utf8: - // Already in UTF-8: No conversion is required - return source; + return "UTF-8"; case Encoding_Ascii: - return ConvertToAscii(source); + return "ASCII"; case Encoding_Latin1: - encoding = "ISO-8859-1"; + return "ISO-8859-1"; break; case Encoding_Latin2: - encoding = "ISO-8859-2"; + return "ISO-8859-2"; break; case Encoding_Latin3: - encoding = "ISO-8859-3"; + return "ISO-8859-3"; break; case Encoding_Latin4: - encoding = "ISO-8859-4"; + return "ISO-8859-4"; break; case Encoding_Latin5: - encoding = "ISO-8859-9"; + return "ISO-8859-9"; break; case Encoding_Cyrillic: - encoding = "ISO-8859-5"; + return "ISO-8859-5"; + break; + + case Encoding_Windows1251: + return "WINDOWS-1251"; break; case Encoding_Arabic: - encoding = "ISO-8859-6"; + return "ISO-8859-6"; break; case Encoding_Greek: - encoding = "ISO-8859-7"; + return "ISO-8859-7"; break; case Encoding_Hebrew: - encoding = "ISO-8859-8"; + return "ISO-8859-8"; break; case Encoding_Japanese: - encoding = "SHIFT-JIS"; + return "SHIFT-JIS"; break; case Encoding_Chinese: - encoding = "GB18030"; + return "GB18030"; break; case Encoding_Thai: - encoding = "TIS620.2533-0"; + return "TIS620.2533-0"; break; default: throw OrthancException(ErrorCode_NotImplemented); } + } + + + std::string Toolbox::ConvertToUtf8(const std::string& source, + Encoding sourceEncoding) + { + if (sourceEncoding == Encoding_Utf8) + { + // Already in UTF-8: No conversion is required + return source; + } + + if (sourceEncoding == Encoding_Ascii) + { + return ConvertToAscii(source); + } + + const char* encoding = GetBoostLocaleEncoding(sourceEncoding); try { @@ -626,6 +707,34 @@ } + std::string Toolbox::ConvertFromUtf8(const std::string& source, + Encoding targetEncoding) + { + if (targetEncoding == Encoding_Utf8) + { + // Already in UTF-8: No conversion is required + return source; + } + + if (targetEncoding == Encoding_Ascii) + { + return ConvertToAscii(source); + } + + const char* encoding = GetBoostLocaleEncoding(targetEncoding); + + try + { + return boost::locale::conv::from_utf(source, encoding); + } + catch (std::runtime_error&) + { + // Bad input string or bad encoding + return ConvertToAscii(source); + } + } + + std::string Toolbox::ConvertToAscii(const std::string& source) { std::string result; @@ -633,7 +742,7 @@ result.reserve(source.size() + 1); for (size_t i = 0; i < source.size(); i++) { - if (source[i] < 128 && source[i] >= 0 && !iscntrl(source[i])) + if (source[i] <= 127 && source[i] >= 0 && !iscntrl(source[i])) { result.push_back(source[i]); } @@ -668,9 +777,46 @@ digest[4]); } - bool Toolbox::IsSHA1(const std::string& str) + bool Toolbox::IsSHA1(const char* str, + size_t size) { - if (str.size() != 44) + if (size == 0) + { + return false; + } + + const char* start = str; + const char* end = str + size; + + // Trim the beginning of the string + while (start < end) + { + if (*start == '\0' || + isspace(*start)) + { + start++; + } + else + { + break; + } + } + + // Trim the trailing of the string + while (start < end) + { + if (*(end - 1) == '\0' || + isspace(*(end - 1))) + { + end--; + } + else + { + break; + } + } + + if (end - start != 44) { return false; } @@ -682,12 +828,12 @@ i == 26 || i == 35) { - if (str[i] != '-') + if (start[i] != '-') return false; } else { - if (!isalnum(str[i])) + if (!isalnum(start[i])) return false; } } @@ -695,12 +841,44 @@ return true; } + + bool Toolbox::IsSHA1(const std::string& s) + { + if (s.size() == 0) + { + return false; + } + else + { + return IsSHA1(s.c_str(), s.size()); + } + } + + +#if BOOST_HAS_DATE_TIME == 1 std::string Toolbox::GetNowIsoString() { boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); return boost::posix_time::to_iso_string(now); } + void Toolbox::GetNowDicom(std::string& date, + std::string& time) + { + boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + tm tm = boost::posix_time::to_tm(now); + + char s[32]; + sprintf(s, "%04d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + date.assign(s); + + // TODO milliseconds + sprintf(s, "%02d%02d%02d.%06d", tm.tm_hour, tm.tm_min, tm.tm_sec, 0); + time.assign(s); + } +#endif + + std::string Toolbox::StripSpaces(const std::string& source) { size_t first = 0; @@ -801,6 +979,7 @@ } +#if BOOST_HAS_REGEX == 1 std::string Toolbox::WildcardToRegularExpression(const std::string& source) { // TODO - Speed up this with a regular expression @@ -828,6 +1007,7 @@ return result; } +#endif @@ -856,40 +1036,20 @@ } - void Toolbox::DecodeDataUriScheme(std::string& mime, - std::string& content, - const std::string& source) - { - boost::regex pattern("data:([^;]+);base64,([a-zA-Z0-9=+/]*)", - boost::regex::icase /* case insensitive search */); - - boost::cmatch what; - if (regex_match(source.c_str(), what, pattern)) - { - mime = what[1]; - content = what[2]; - } - else - { - throw OrthancException(ErrorCode_BadFileFormat); - } - } - - - void Toolbox::CreateDirectory(const std::string& path) + void Toolbox::MakeDirectory(const std::string& path) { if (boost::filesystem::exists(path)) { if (!boost::filesystem::is_directory(path)) { - throw OrthancException("Cannot create the directory over an existing file: " + path); + throw OrthancException(ErrorCode_DirectoryOverFile); } } else { if (!boost::filesystem::create_directories(path)) { - throw OrthancException("Unable to create the directory: " + path); + throw OrthancException(ErrorCode_MakeDirectory); } } } @@ -1050,7 +1210,10 @@ if (pid == -1) { // Error in fork() +#if ORTHANC_ENABLE_LOGGING == 1 LOG(ERROR) << "Cannot fork a child process"; +#endif + throw OrthancException(ErrorCode_SystemCommand); } else if (pid == 0) @@ -1070,7 +1233,10 @@ if (status != 0) { +#if ORTHANC_ENABLE_LOGGING == 1 LOG(ERROR) << "System command failed with status code " << status; +#endif + throw OrthancException(ErrorCode_SystemCommand); } } @@ -1108,5 +1274,89 @@ return true; } + + + void Toolbox::CopyJsonWithoutComments(Json::Value& target, + const Json::Value& source) + { + switch (source.type()) + { + case Json::nullValue: + target = Json::nullValue; + break; + + case Json::intValue: + target = source.asInt64(); + break; + + case Json::uintValue: + target = source.asUInt64(); + break; + + case Json::realValue: + target = source.asDouble(); + break; + + case Json::stringValue: + target = source.asString(); + break; + + case Json::booleanValue: + target = source.asBool(); + break; + + case Json::arrayValue: + { + target = Json::arrayValue; + for (Json::Value::ArrayIndex i = 0; i < source.size(); i++) + { + Json::Value& item = target.append(Json::nullValue); + CopyJsonWithoutComments(item, source[i]); + } + + break; + } + + case Json::objectValue: + { + target = Json::objectValue; + Json::Value::Members members = source.getMemberNames(); + for (Json::Value::ArrayIndex i = 0; i < members.size(); i++) + { + const std::string item = members[i]; + CopyJsonWithoutComments(target[item], source[item]); + } + + break; + } + + default: + break; + } + } + + + bool Toolbox::StartsWith(const std::string& str, + const std::string& prefix) + { + if (str.size() < prefix.size()) + { + return false; + } + else + { + return str.compare(0, prefix.size(), prefix) == 0; + } + } + + + int Toolbox::GetProcessId() + { +#if defined(_WIN32) + return static_cast(_getpid()); +#else + return static_cast(getpid()); +#endif + } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Toolbox.h --- a/Core/Toolbox.h Wed Feb 11 10:40:08 2015 +0100 +++ b/Core/Toolbox.h Wed Sep 30 13:23:31 2015 +0200 @@ -69,6 +69,10 @@ void WriteFile(const std::string& content, const std::string& path); + void WriteFile(const void* content, + size_t size, + const std::string& path); + void USleep(uint64_t microSeconds); void RemoveFile(const std::string& path); @@ -90,53 +94,72 @@ uint64_t GetFileSize(const std::string& path); +#if !defined(ORTHANC_ENABLE_MD5) || ORTHANC_ENABLE_MD5 == 1 void ComputeMD5(std::string& result, const std::string& data); void ComputeMD5(std::string& result, const void* data, size_t length); +#endif void ComputeSHA1(std::string& result, const std::string& data); - bool IsSHA1(const std::string& str); + bool IsSHA1(const char* str, + size_t size); + bool IsSHA1(const std::string& s); + +#if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1 void DecodeBase64(std::string& result, const std::string& data); void EncodeBase64(std::string& result, const std::string& data); +# if BOOST_HAS_REGEX == 1 + void DecodeDataUriScheme(std::string& mime, + std::string& content, + const std::string& source); +# endif +#endif + std::string GetPathToExecutable(); std::string GetDirectoryOfExecutable(); std::string ConvertToUtf8(const std::string& source, - const Encoding sourceEncoding); + Encoding sourceEncoding); + + std::string ConvertFromUtf8(const std::string& source, + Encoding targetEncoding); std::string ConvertToAscii(const std::string& source); std::string StripSpaces(const std::string& source); +#if BOOST_HAS_DATE_TIME == 1 std::string GetNowIsoString(); + void GetNowDicom(std::string& date, + std::string& time); +#endif + // In-place percent-decoding for URL void UrlDecode(std::string& s); Endianness DetectEndianness(); +#if BOOST_HAS_REGEX == 1 std::string WildcardToRegularExpression(const std::string& s); +#endif void TokenizeString(std::vector& result, const std::string& source, char separator); - void DecodeDataUriScheme(std::string& mime, - std::string& content, - const std::string& source); - - void CreateDirectory(const std::string& path); + void MakeDirectory(const std::string& path); bool IsExistingFile(const std::string& path); @@ -151,5 +174,13 @@ const std::vector& arguments); bool IsInteger(const std::string& str); + + void CopyJsonWithoutComments(Json::Value& target, + const Json::Value& source); + + bool StartsWith(const std::string& str, + const std::string& prefix); + + int GetProcessId(); } } diff -r 61ce8147f30d -r 4a5c79e31b60 Core/Uuid.cpp diff -r 61ce8147f30d -r 4a5c79e31b60 INSTALL --- a/INSTALL Wed Feb 11 10:40:08 2015 +0100 +++ b/INSTALL Wed Sep 30 13:23:31 2015 +0200 @@ -32,12 +32,24 @@ To build Orthanc, you must: 1) Download the source code (either using Mercurial, or through the - released versions). For the examples below, we assume the source + official releases). For the examples below, we assume the source directory is "~/Orthanc". 2) Create a build directory. For the examples below, we assume the build directory is "~/OrthancBuild". +3) Depending on your platform, follow the build instructions below. + + +WARNING 1: If you do not create a fresh "~/OrthancBuild" directory +after upgrading the source code (i.e. if you reuse the build directory +that was used to build a different version of Orthanc), the build +might fail because of changes in the compilation/linking flags. Always +prefer to force a re-build in a new directory. + +WARNING 2: If cmake complains about not being able to uncompress +third-party dependencies, delete the "~/Orthanc/ThirdPartyDownloads/" +folder, then restart cmake. Native Linux Compilation diff -r 61ce8147f30d -r 4a5c79e31b60 LinuxCompilation.txt --- a/LinuxCompilation.txt Wed Feb 11 10:40:08 2015 +0100 +++ b/LinuxCompilation.txt Wed Sep 30 13:23:31 2015 +0200 @@ -74,50 +74,12 @@ Orthanc users. -SUPPORTED - Debian Squeeze (6.x) --------------------------------- - -# sudo apt-get install build-essential unzip cmake mercurial \ - uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \ - libgoogle-glog-dev libpng-dev libgtest-dev \ - libsqlite3-dev libssl-dev zlib1g-dev - -# cmake -DALLOW_DOWNLOADS=ON \ - -DUSE_SYSTEM_BOOST=OFF \ - -DUSE_SYSTEM_DCMTK=OFF \ - -DUSE_SYSTEM_MONGOOSE=OFF \ - -DUSE_SYSTEM_JSONCPP=OFF \ - -DUSE_SYSTEM_PUGIXML=OFF \ - -DENABLE_JPEG=OFF \ - -DENABLE_JPEG_LOSSLESS=OFF \ - ~/Orthanc - - -SUPPORTED - Debian Wheezy (7.x) -------------------------------- - -# sudo apt-get install build-essential unzip cmake mercurial \ - uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \ - libgtest-dev libpng-dev libsqlite3-dev \ - libssl-dev zlib1g-dev libdcmtk2-dev \ - libboost-all-dev libwrap0-dev libjsoncpp-dev - -# cmake -DALLOW_DOWNLOADS=ON \ - -DUSE_SYSTEM_GOOGLE_LOG=OFF \ - -DUSE_SYSTEM_MONGOOSE=OFF \ - -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \ - -DUSE_SYSTEM_PUGIXML=OFF \ - -DENABLE_JPEG=OFF \ - -DENABLE_JPEG_LOSSLESS=OFF \ - ~/Orthanc - - SUPPORTED - Debian Jessie/Sid ----------------------------- # sudo apt-get install build-essential unzip cmake mercurial \ uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \ - libgoogle-glog-dev libgtest-dev libpng-dev \ + libgoogle-glog-dev libgtest-dev libpng-dev libjpeg-dev \ libsqlite3-dev libssl-dev zlib1g-dev libdcmtk2-dev \ libboost-all-dev libwrap0-dev libjsoncpp-dev libpugixml-dev @@ -131,15 +93,16 @@ http://anonscm.debian.org/viewvc/debian-med/trunk/packages/orthanc/trunk/debian/ -SUPPORTED - Ubuntu 12.04 LTS ----------------------------- +SUPPORTED - Ubuntu 12.04.5 LTS +------------------------------ # sudo apt-get install build-essential unzip cmake mercurial \ uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \ - libgtest-dev libpng-dev libsqlite3-dev libssl-dev \ - zlib1g-dev libdcmtk2-dev libboost-all-dev libwrap0-dev + libgtest-dev libpng-dev libsqlite3-dev libssl-dev libjpeg-dev \ + zlib1g-dev libdcmtk2-dev libboost1.48-all-dev libwrap0-dev \ + libcharls-dev -# cmake "-DDCMTK_LIBRARIES=wrap;oflog" \ +# cmake "-DDCMTK_LIBRARIES=boost_locale;CharLS;dcmjpls;wrap;oflog" \ -DALLOW_DOWNLOADS=ON \ -DUSE_SYSTEM_MONGOOSE=OFF \ -DUSE_SYSTEM_JSONCPP=OFF \ @@ -149,67 +112,36 @@ ~/Orthanc -SUPPORTED - Ubuntu 12.10 ------------------------- + +SUPPORTED - Ubuntu 14.04 LTS +---------------------------- # sudo apt-get install build-essential unzip cmake mercurial \ uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \ - libgoogle-glog-dev libgtest-dev libpng-dev \ - libsqlite3-dev libssl-dev zlib1g-dev \ - libdcmtk2-dev libboost-all-dev libwrap0-dev libcharls-dev - -With JPEG: + libgtest-dev libpng-dev libsqlite3-dev libssl-dev libjpeg-dev \ + zlib1g-dev libdcmtk2-dev libboost-all-dev libwrap0-dev \ + libcharls-dev libjsoncpp-dev libpugixml-dev -# cmake "-DDCMTK_LIBRARIES=CharLS;dcmjpls;wrap;oflog" \ - -DALLOW_DOWNLOADS=ON \ - -DUSE_SYSTEM_MONGOOSE=OFF \ - -DUSE_SYSTEM_JSONCPP=OFF \ - -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \ - -DUSE_SYSTEM_PUGIXML=OFF \ - ~/Orthanc - - -Without JPEG: - -# cmake "-DDCMTK_LIBRARIES=wrap;oflog" \ - -DALLOW_DOWNLOADS=ON \ - -DUSE_SYSTEM_MONGOOSE=OFF \ - -DUSE_SYSTEM_JSONCPP=OFF \ +# cmake -DALLOW_DOWNLOADS=ON \ -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \ - -DUSE_SYSTEM_PUGIXML=OFF \ - -DENABLE_JPEG=OFF \ - -DENABLE_JPEG_LOSSLESS=OFF \ - ~/Orthanc - - -SUPPORTED - Ubuntu 13.10 ------------------------- - -# sudo apt-get install build-essential unzip cmake mercurial \ - uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \ - libgoogle-glog-dev libgtest-dev libpng-dev \ - libsqlite3-dev libssl-dev zlib1g-dev \ - libdcmtk2-dev libboost-all-dev libwrap0-dev libjsoncpp-dev - -# cmake "-DDCMTK_LIBRARIES=wrap;oflog" \ - -DALLOW_DOWNLOADS=ON \ - -DUSE_SYSTEM_MONGOOSE=OFF \ - -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \ - -DUSE_SYSTEM_PUGIXML=OFF \ - -DENABLE_JPEG=OFF \ - -DENABLE_JPEG_LOSSLESS=OFF \ - ~/Orthanc + -DUSE_SYSTEM_MONGOOSE=OFF \ + -DDCMTK_LIBRARIES=dcmjpls \ + ~/Orthanc -SUPPORTED - Fedora 19/20 +SUPPORTED - Fedora 20-22 ------------------------ -# sudo yum install make automake gcc gcc-c++ python cmake \ +# sudo yum install unzip make automake gcc gcc-c++ python cmake \ boost-devel curl-devel dcmtk-devel glog-devel \ - gtest-devel libpng-devel libsqlite3x-devel libuuid-devel \ + gtest-devel libpng-devel libsqlite3x-devel libuuid-devel jpeg-devel \ mongoose-devel openssl-devel jsoncpp-devel lua-devel pugixml-devel +You will also have to install "gflags-devel" on Fedora 21&22: + +# sudo yum install gflags-devel + # cmake "-DDCMTK_LIBRARIES=CharLS" \ -DSYSTEM_MONGOOSE_USE_CALLBACKS=OFF \ ~/Orthanc @@ -219,6 +151,37 @@ +SUPPORTED - FreeBSD 10.1 +------------------------ + +# pkg install jsoncpp pugixml lua51 curl googletest dcmtk cmake jpeg \ + e2fsprogs-libuuid glog boost-libs sqlite3 python libiconv + +# cmake -DALLOW_DOWNLOADS=ON \ + -DUSE_SYSTEM_MONGOOSE=OFF \ + -DDCMTK_LIBRARIES="dcmdsig;charls;dcmjpls" \ + ~/Orthanc + + + +SUPPORTED - CentOS 6 +-------------------- + +# yum install unzip make automake gcc gcc-c++ python cmake curl-devel \ + libpng-devel sqlite-devel libuuid-devel openssl-devel \ + lua-devel mercurial patch tar + +# cmake -DALLOW_DOWNLOADS=ON \ + -DUSE_SYSTEM_JSONCPP=OFF \ + -DUSE_SYSTEM_MONGOOSE=OFF \ + -DUSE_SYSTEM_PUGIXML=OFF \ + -DUSE_SYSTEM_SQLITE=OFF \ + -DUSE_SYSTEM_BOOST=OFF \ + -DUSE_SYSTEM_DCMTK=OFF \ + -DUSE_SYSTEM_GOOGLE_TEST=OFF \ + -DUSE_SYSTEM_LIBJPEG=OFF \ + ~/Orthanc + Other Linux distributions? diff -r 61ce8147f30d -r 4a5c79e31b60 NEWS --- a/NEWS Wed Feb 11 10:40:08 2015 +0100 +++ b/NEWS Wed Sep 30 13:23:31 2015 +0200 @@ -1,6 +1,160 @@ Pending changes in the mainline =============================== +* Add ".dcm" suffix to files in ZIP archives (cf. URI ".../archive") +* "/tools/create-dicom": Support of binary tags encoded using data URI scheme + +Plugins +------- + +* New function "OrthancPluginRegisterErrorCode()" to declare custom error codes +* New function "OrthancPluginRegisterDictionaryTag()" to declare DICOM tags + +Lua +--- + +* Optional argument "keepStrings" in "DumpJson()" + +Maintenance +----------- + +* "/system" URI gives information about the plugins used for storage area and DB back-end +* Plugin callbacks should now return explicit "OrthancPluginErrorCode" instead of integers +* "/tools/create-dicom" can create tags with unknown VR + + +Version 0.9.4 (2015/09/16) +========================== + +* Preview of PDF files encapsulated in DICOM from Orthanc Explorer +* Creation of DICOM files with encapsulated PDF through "/tools/create-dicom" +* "limit" and "since" arguments while retrieving DICOM resources in the REST API +* Support of "deflate" and "gzip" content-types in HTTP requests +* Options to validate peers against CA certificates in HTTPS requests +* New configuration option: "HttpTimeout" to set the default timeout for HTTP requests + +Lua +--- + +* More information about the origin request in the "OnStoredInstance()" and + "ReceivedInstanceFilter()" callbacks. WARNING: This can result in + incompatibilities wrt. previous versions of Orthanc. +* New function "GetOrthancConfiguration()" to get the Orthanc configuration + +Plugins +------- + +* New functions to compress/uncompress images using PNG and JPEG +* New functions to issue HTTP requests from plugins +* New function "OrthancPluginBufferCompression()" to (un)compress memory buffers +* New function "OrthancPluginReadFile()" to read files from the filesystem +* New function "OrthancPluginWriteFile()" to write files to the filesystem +* New function "OrthancPluginGetErrorDescription()" to convert error codes to strings +* New function "OrthancPluginSendHttpStatus()" to send HTTP status with a body +* New function "OrthancPluginRegisterRestCallbackNoLock()" for high-performance plugins +* Plugins have access to explicit error codes +* Improvements to the sample "ServeFolders" plugin +* Primitives to upgrade the database version in plugins + +Maintenance +----------- + +* Many code refactorings +* Improved error codes (no more custom descriptions in exceptions) +* If error while calling the REST API, the answer body contains description of the error + (this feature can be disabled with the "HttpDescribeErrors" option) +* Upgrade to curl 7.44.0 for static and Windows builds +* Upgrade to libcurl 1.0.2d for static and Windows builds +* Depends on libjpeg 9a +* Bypass zlib uncompression if "StorageCompression" is enabled and HTTP client supports deflate + + +Version 0.9.3 (2015/08/07) +========================== + +* C-Echo testing can be triggered from Orthanc Explorer (in the query/retrieve page) +* Removal of the dependency upon Google Log, Orthanc now uses its internal logger + (use -DENABLE_GOOGLE_LOG=ON to re-enable Google Log) +* Upgrade to JsonCpp 0.10.5 for static and Windows builds + + +Version 0.9.2 (2015/08/02) +========================== + +* Upgrade to Boost 1.58.0 for static and Windows builds +* Source code repository moved from Google Code to BitBucket +* Inject version information into Windows binaries +* Fix access to binary data in HTTP/REST requests by Lua scripts +* Fix potential deadlock in the callbacks of plugins + + +Version 0.9.1 (2015/07/02) +========================== + +General +------- + +* The configuration can be splitted into several files stored inside the same folder +* Custom setting of the local AET during C-Store SCU (both in Lua and in the REST API) +* Many code refactorings + +Lua +--- + +* Access to the REST API of Orthanc (RestApiGet, RestApiPost, RestApiPut, RestApiDelete) +* Functions to convert between Lua values and JSON strings: "ParseJson" and "DumpJson" +* New events: "OnStablePatient", "OnStableStudy", "OnStableSeries", "Initialize", "Finalize" + +Plugins +------- + +* Plugins can retrieve the configuration file directly as a JSON string +* Plugins can send answers as multipart messages + +Fixes +----- + +* Fix compatibility issues for C-Find SCU to Siemens Syngo.Via modalities SCP +* Fix issue 15 (Lua scripts making HTTP requests) +* Fix issue 35 (Characters in PatientID string are not protected for C-Find) +* Fix issue 37 (Hyphens trigger range query even if datatype does not support ranges) + + +Version 0.9.0 (2015/06/03) +========================== + +Major +----- + +* DICOM Query/Retrieve available from Orthanc Explorer +* C-Move SCU and C-Find SCU are accessible through the REST API +* "?expand" flag for URIs "/patients", "/studies" and "/series" +* "/tools/find" URI to search for DICOM resources from REST +* Support of FreeBSD +* The "Orthanc Client" SDK is now a separate project + +Minor +----- + +* Speed-up in Orthanc Explorer for large amount of images +* Speed-up of the C-Find SCP server of Orthanc +* Allow replacing PatientID/StudyInstanceUID/SeriesInstanceUID from Lua scripts +* Option "CaseSensitivePN" to enable case-insensitive C-Find SCP + +Fixes +----- + +* Prevent freeze on C-FIND if no DICOM tag is to be returned +* Fix slow C-Store SCP on recent versions of Linux, if + USE_SYSTEM_DCMTK is set to OFF (http://forum.dcmtk.org/viewtopic.php?f=1&t=4009) +* Fix issue 30 (QR response missing "Query/Retrieve Level" (008,0052)) +* Fix issue 32 (Cyrillic symbols): Introduction of the "Windows1251" encoding +* Plugins now receive duplicated GET arguments in their REST callbacks + + +Version 0.8.6 (2015/02/12) +========================== + Major ----- @@ -15,6 +169,7 @@ * More flexible "/modify" and "/anonymize" for single instance * Access to called AET and remote AET from Lua scripts ("OnStoredInstance") * Option "DicomAssociationCloseDelay" to set delay before closing DICOM association +* ZIP archives now display the accession number of the studies Plugins ------- @@ -32,6 +187,7 @@ * Code refactorings * Fix issue 25 (AET with underscore not allowed) * Fix replacement and insertion of private DICOM tags +* Fix anonymization generating non-portable DICOM files Version 0.8.5 (2014/11/04) diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/Instance.cpp --- a/OrthancCppClient/Instance.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,286 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../Core/PrecompiledHeaders.h" -#include "Instance.h" - -#include "OrthancConnection.h" - -#include - -namespace OrthancClient -{ - void Instance::DownloadImage() - { - if (reader_.get() == NULL) - { - const char* suffix; - switch (mode_) - { - case Orthanc::ImageExtractionMode_Preview: - suffix = "preview"; - break; - - case Orthanc::ImageExtractionMode_UInt8: - suffix = "image-uint8"; - break; - - case Orthanc::ImageExtractionMode_UInt16: - suffix = "image-uint16"; - break; - - case Orthanc::ImageExtractionMode_Int16: - suffix = "image-int16"; - break; - - default: - throw OrthancClientException(Orthanc::ErrorCode_NotImplemented); - } - - Orthanc::HttpClient client(connection_.GetHttpClient()); - client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/instances/" + id_ + "/" + suffix); - std::string png; - - if (!client.Apply(png)) - { - throw OrthancClientException(Orthanc::ErrorCode_NotImplemented); - } - - reader_.reset(new Orthanc::PngReader); - reader_->ReadFromMemory(png); - } - } - - void Instance::DownloadDicom() - { - if (dicom_.get() == NULL) - { - Orthanc::HttpClient client(connection_.GetHttpClient()); - client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/instances/" + id_ + "/file"); - - dicom_.reset(new std::string); - - if (!client.Apply(*dicom_)) - { - throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol); - } - } - } - - Instance::Instance(const OrthancConnection& connection, - const char* id) : - connection_(connection), - id_(id), - mode_(Orthanc::ImageExtractionMode_Int16) - { - Orthanc::HttpClient client(connection_.GetHttpClient()); - - client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/instances/" + id_ + "/simplified-tags"); - Json::Value v; - if (!client.Apply(tags_)) - { - throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol); - } - } - - const char* Instance::GetTagAsString(const char* tag) const - { - if (tags_.isMember(tag)) - { - return tags_[tag].asCString(); - } - else - { - throw OrthancClientException(Orthanc::ErrorCode_InexistentItem); - } - } - - float Instance::GetTagAsFloat(const char* tag) const - { - std::string value = GetTagAsString(tag); - - try - { - return boost::lexical_cast(value); - } - catch (boost::bad_lexical_cast) - { - throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat); - } - } - - int Instance::GetTagAsInt(const char* tag) const - { - std::string value = GetTagAsString(tag); - - try - { - return boost::lexical_cast(value); - } - catch (boost::bad_lexical_cast) - { - throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat); - } - } - - unsigned int Instance::GetWidth() - { - DownloadImage(); - return reader_->GetWidth(); - } - - unsigned int Instance::GetHeight() - { - DownloadImage(); - return reader_->GetHeight(); - } - - unsigned int Instance::GetPitch() - { - DownloadImage(); - return reader_->GetPitch(); - } - - Orthanc::PixelFormat Instance::GetPixelFormat() - { - DownloadImage(); - return reader_->GetFormat(); - } - - const void* Instance::GetBuffer() - { - DownloadImage(); - return reader_->GetConstBuffer(); - } - - const void* Instance::GetBuffer(unsigned int y) - { - DownloadImage(); - return reader_->GetConstRow(y); - } - - void Instance::DiscardImage() - { - reader_.reset(); - } - - void Instance::DiscardDicom() - { - dicom_.reset(); - } - - - void Instance::SetImageExtractionMode(Orthanc::ImageExtractionMode mode) - { - if (mode_ == mode) - { - return; - } - - DiscardImage(); - mode_ = mode; - } - - - void Instance::SplitVectorOfFloats(std::vector& target, - const char* tag) - { - const std::string value = GetTagAsString(tag); - - target.clear(); - - try - { - std::string tmp; - for (size_t i = 0; i < value.size(); i++) - { - if (value[i] == '\\') - { - target.push_back(boost::lexical_cast(tmp)); - tmp.clear(); - } - else - { - tmp.push_back(value[i]); - } - } - - target.push_back(boost::lexical_cast(tmp)); - } - catch (boost::bad_lexical_cast) - { - // Unable to parse the Image Orientation Patient. - throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat); - } - } - - - const uint64_t Instance::GetDicomSize() - { - DownloadDicom(); - assert(dicom_.get() != NULL); - return dicom_->size(); - } - - const void* Instance::GetDicom() - { - DownloadDicom(); - assert(dicom_.get() != NULL); - - if (dicom_->size() == 0) - { - return NULL; - } - else - { - return &((*dicom_) [0]); - } - } - - - void Instance::LoadTagContent(const char* path) - { - Orthanc::HttpClient client(connection_.GetHttpClient()); - client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/instances/" + id_ + "/content/" + path); - - if (!client.Apply(content_)) - { - throw OrthancClientException(Orthanc::ErrorCode_UnknownResource); - } - } - - - const char* Instance::GetLoadedTagContent() const - { - return content_.c_str(); - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/Instance.h --- a/OrthancCppClient/Instance.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include -#include - -#include "OrthancClientException.h" -#include "../Core/IDynamicObject.h" -#include "../Core/ImageFormats/PngReader.h" - -namespace OrthancClient -{ - class OrthancConnection; - - /** - * {summary}{Connection to an instance stored in %Orthanc.} - * {description}{This class encapsulates a connection to an image instance - * from a remote instance of %Orthanc.} - **/ - class LAAW_API Instance : public Orthanc::IDynamicObject - { - private: - const OrthancConnection& connection_; - std::string id_; - Json::Value tags_; - std::auto_ptr reader_; - Orthanc::ImageExtractionMode mode_; - std::auto_ptr dicom_; - std::string content_; - - void DownloadImage(); - - void DownloadDicom(); - - public: - /** - * {summary}{Create a connection to some image instance.} - * {param}{connection The remote instance of %Orthanc.} - * {param}{id The %Orthanc identifier of the image instance.} - **/ - Instance(const OrthancConnection& connection, - const char* id); - - - /** - * {summary}{Get the %Orthanc identifier of this identifier.} - * {returns}{The identifier.} - **/ - const char* GetId() const - { - return id_.c_str(); - } - - - /** - * {summary}{Set the extraction mode for the 2D image corresponding to this instance.} - * {param}{mode The extraction mode.} - **/ - void SetImageExtractionMode(Orthanc::ImageExtractionMode mode); - - /** - * {summary}{Get the extraction mode for the 2D image corresponding to this instance.} - * {returns}{The extraction mode.} - **/ - Orthanc::ImageExtractionMode GetImageExtractionMode() const - { - return mode_; - } - - - /** - * {summary}{Get the string value of some DICOM tag of this instance.} - * {param}{tag The name of the tag of interest.} - * {returns}{The value of the tag.} - **/ - const char* GetTagAsString(const char* tag) const; - - /** - * {summary}{Get the floating point value that is stored in some DICOM tag of this instance.} - * {param}{tag The name of the tag of interest.} - * {returns}{The value of the tag.} - **/ - float GetTagAsFloat(const char* tag) const; - - /** - * {summary}{Get the integer value that is stored in some DICOM tag of this instance.} - * {param}{tag The name of the tag of interest.} - * {returns}{The value of the tag.} - **/ - int32_t GetTagAsInt(const char* tag) const; - - - /** - * {summary}{Get the width of the 2D image.} - * {description}{Get the width of the 2D image that is encoded by this DICOM instance.} - * {returns}{The width.} - **/ - uint32_t GetWidth(); - - /** - * {summary}{Get the height of the 2D image.} - * {description}{Get the height of the 2D image that is encoded by this DICOM instance.} - * {returns}{The height.} - **/ - uint32_t GetHeight(); - - /** - * {summary}{Get the number of bytes between two lines of the image (pitch).} - * {description}{Get the number of bytes between two lines of the image in the memory buffer returned by GetBuffer(). This value depends on the extraction mode for the image.} - * {returns}{The pitch.} - **/ - uint32_t GetPitch(); - - /** - * {summary}{Get the format of the pixels of the 2D image.} - * {description}{Return the memory layout that is used for the 2D image that is encoded by this DICOM instance. This value depends on the extraction mode for the image.} - * {returns}{The pixel format.} - **/ - Orthanc::PixelFormat GetPixelFormat(); - - /** - * {summary}{Access the memory buffer in which the raw pixels of the 2D image are stored.} - * {returns}{A pointer to the memory buffer.} - **/ - const void* GetBuffer(); - - /** - * {summary}{Access the memory buffer in which the raw pixels of some line of the 2D image are stored.} - * {param}{y The line of interest.} - * {returns}{A pointer to the memory buffer.} - **/ - const void* GetBuffer(uint32_t y); - - /** - * {summary}{Get the size of the DICOM file corresponding to this instance.} - * {returns}{The file size.} - **/ - const uint64_t GetDicomSize(); - - /** - * {summary}{Get a pointer to the content of the DICOM file corresponding to this instance.} - * {returns}{The DICOM file.} - **/ - const void* GetDicom(); - - /** - * {summary}{Discard the downloaded 2D image, so as to make room in memory.} - **/ - void DiscardImage(); - - /** - * {summary}{Discard the downloaded DICOM file, so as to make room in memory.} - **/ - void DiscardDicom(); - - LAAW_API_INTERNAL void SplitVectorOfFloats(std::vector& target, - const char* tag); - - /** - * {summary}{Load a raw tag from the DICOM file.} - * {param}{path The path to the tag of interest (e.g. "0020-000d").} - **/ - void LoadTagContent(const char* path); - - /** - * {summary}{Return the value of the raw tag that was loaded by LoadContent.} - * {returns}{The tag value.} - **/ - const char* GetLoadedTagContent() const; - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/OrthancClientException.h --- a/OrthancCppClient/OrthancClientException.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../Core/OrthancException.h" -#include - -namespace OrthancClient -{ - class OrthancClientException : public ::Laaw::LaawException - { - public: - OrthancClientException(Orthanc::ErrorCode code) : - LaawException(Orthanc::OrthancException::GetDescription(code)) - { - } - - OrthancClientException(const char* message) : - LaawException(message) - { - } - - OrthancClientException(const std::string& message) : - LaawException(message) - { - } - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/OrthancConnection.cpp --- a/OrthancCppClient/OrthancConnection.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../Core/PrecompiledHeaders.h" -#include "OrthancConnection.h" - -#include "../Core/Toolbox.h" - -namespace OrthancClient -{ - void OrthancConnection::ReadPatients() - { - client_.SetMethod(Orthanc::HttpMethod_Get); - client_.SetUrl(orthancUrl_ + "/patients"); - - Json::Value v; - if (!client_.Apply(content_)) - { - throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol); - } - } - - Orthanc::IDynamicObject* OrthancConnection::GetFillerItem(size_t index) - { - Json::Value::ArrayIndex tmp = static_cast(index); - std::string id = content_[tmp].asString(); - return new Patient(*this, id.c_str()); - } - - Patient& OrthancConnection::GetPatient(unsigned int index) - { - return dynamic_cast(patients_.GetItem(index)); - } - - OrthancConnection::OrthancConnection(const char* orthancUrl) : - orthancUrl_(orthancUrl), patients_(*this) - { - ReadPatients(); - } - - OrthancConnection::OrthancConnection(const char* orthancUrl, - const char* username, - const char* password) : - orthancUrl_(orthancUrl), patients_(*this) - { - client_.SetCredentials(username, password); - ReadPatients(); - } - - - void OrthancConnection::Store(const void* dicom, uint64_t size) - { - if (size == 0) - { - return; - } - - client_.SetMethod(Orthanc::HttpMethod_Post); - client_.SetUrl(orthancUrl_ + "/instances"); - - // Copy the DICOM file in the POST body. TODO - Avoid memory copy - client_.AccessPostData().resize(static_cast(size)); - memcpy(&client_.AccessPostData()[0], dicom, static_cast(size)); - - Json::Value v; - if (!client_.Apply(v)) - { - throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol); - } - - Reload(); - } - - - void OrthancConnection::StoreFile(const char* filename) - { - std::string content; - Orthanc::Toolbox::ReadFile(content, filename); - - if (content.size() != 0) - { - Store(&content[0], content.size()); - } - } - -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/OrthancConnection.h --- a/OrthancCppClient/OrthancConnection.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,180 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../Core/HttpClient.h" - -#include "Patient.h" - -namespace OrthancClient -{ - /** - * {summary}{Connection to an instance of %Orthanc.} - * {description}{This class encapsulates a connection to a remote instance - * of %Orthanc through its REST API.} - **/ - class LAAW_API OrthancConnection : - public boost::noncopyable, - private Orthanc::ArrayFilledByThreads::IFiller - { - private: - Orthanc::HttpClient client_; - std::string orthancUrl_; - Orthanc::ArrayFilledByThreads patients_; - Json::Value content_; - - void ReadPatients(); - - virtual size_t GetFillerSize() - { - return content_.size(); - } - - virtual Orthanc::IDynamicObject* GetFillerItem(size_t index); - - public: - /** - * {summary}{Create a connection to an instance of %Orthanc.} - * {param}{orthancUrl URL to which the REST API of %Orthanc is listening.} - **/ - OrthancConnection(const char* orthancUrl); - - /** - * {summary}{Create a connection to an instance of %Orthanc, with authentication.} - * {param}{orthancUrl URL to which the REST API of %Orthanc is listening.} - * {param}{username The username.} - * {param}{password The password.} - **/ - OrthancConnection(const char* orthancUrl, - const char* username, - const char* password); - - virtual ~OrthancConnection() - { - } - - /** - * {summary}{Returns the number of threads for this connection.} - * {description}{Returns the number of simultaneous connections - * that are used when downloading information from this instance - * of %Orthanc.} - * {returns}{The number of threads.} - **/ - uint32_t GetThreadCount() const - { - return patients_.GetThreadCount(); - } - - /** - * {summary}{Sets the number of threads for this connection.} - * {description}{Sets the number of simultaneous connections - * that are used when downloading information from this instance - * of %Orthanc.} - * {param}{threadCount The number of threads.} - **/ - void SetThreadCount(uint32_t threadCount) - { - patients_.SetThreadCount(threadCount); - } - - /** - * {summary}{Reload the list of the patients.} - * {description}{This method will reload the list of the patients from the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated.} - **/ - void Reload() - { - ReadPatients(); - patients_.Invalidate(); - } - - LAAW_API_INTERNAL const Orthanc::HttpClient& GetHttpClient() const - { - return client_; - } - - /** - * {summary}{Returns the URL of this instance of %Orthanc.} - * {description}{Returns the URL of the remote %Orthanc instance to which this object is connected.} - * {returns}{The URL.} - **/ - const char* GetOrthancUrl() const - { - return orthancUrl_.c_str(); - } - - /** - * {summary}{Returns the number of patients.} - * {description}{Returns the number of patients that are stored in the remote instance of %Orthanc.} - * {returns}{The number of patients.} - **/ - uint32_t GetPatientCount() - { - return patients_.GetSize(); - } - - /** - * {summary}{Get some patient.} - * {description}{This method will return an object that contains information about some patient. The patients are indexed by a number between 0 (inclusive) and the result of GetPatientCount() (exclusive).} - * {param}{index The index of the patient of interest.} - * {returns}{The patient.} - **/ - Patient& GetPatient(uint32_t index); - - /** - * {summary}{Delete some patient.} - * {description}{Delete some patient from the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated.} - * {param}{index The index of the patient of interest.} - * {returns}{The patient.} - **/ - void DeletePatient(uint32_t index) - { - GetPatient(index).Delete(); - Reload(); - } - - /** - * {summary}{Send a DICOM file.} - * {description}{This method will store a DICOM file in the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated.} - * {param}{filename Path to the DICOM file} - **/ - void StoreFile(const char* filename); - - /** - * {summary}{Send a DICOM file that is contained inside a memory buffer.} - * {description}{This method will store a DICOM file in the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated.} - * {param}{dicom The memory buffer containing the DICOM file.} - * {param}{size The size of the DICOM file.} - **/ - void Store(const void* dicom, uint64_t size); - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/OrthancCppClient.cpp --- a/OrthancCppClient/OrthancCppClient.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -/** - * The sources of the C++ client library must be put in this file to - * avoid problems with precompiled headers. - **/ - -#include "../Core/ChunkedBuffer.cpp" -#include "../Core/Enumerations.cpp" -#include "../Core/HttpClient.cpp" -#include "../Core/ImageFormats/ImageAccessor.cpp" -#include "../Core/ImageFormats/ImageBuffer.cpp" -#include "../Core/ImageFormats/PngReader.cpp" -#include "../Core/MultiThreading/ArrayFilledByThreads.cpp" -#include "../Core/MultiThreading/SharedMessageQueue.cpp" -#include "../Core/MultiThreading/ThreadedCommandProcessor.cpp" -#include "../Core/OrthancException.cpp" -#include "../Core/Toolbox.cpp" -#include "../OrthancCppClient/Instance.cpp" -#include "../OrthancCppClient/OrthancConnection.cpp" -#include "../OrthancCppClient/Patient.cpp" -#include "../OrthancCppClient/Series.cpp" -#include "../OrthancCppClient/Study.cpp" diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/Patient.cpp --- a/OrthancCppClient/Patient.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../Core/PrecompiledHeaders.h" -#include "Patient.h" - -#include "OrthancConnection.h" - -namespace OrthancClient -{ - void Patient::ReadPatient() - { - Orthanc::HttpClient client(connection_.GetHttpClient()); - client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/patients/" + id_); - - Json::Value v; - if (!client.Apply(patient_)) - { - throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol); - } - } - - Orthanc::IDynamicObject* Patient::GetFillerItem(size_t index) - { - Json::Value::ArrayIndex tmp = static_cast(index); - std::string id = patient_["Studies"][tmp].asString(); - return new Study(connection_, id.c_str()); - } - - Patient::Patient(const OrthancConnection& connection, - const char* id) : - connection_(connection), - id_(id), - studies_(*this) - { - studies_.SetThreadCount(connection.GetThreadCount()); - ReadPatient(); - } - - const char* Patient::GetMainDicomTag(const char* tag, const char* defaultValue) const - { - if (patient_["MainDicomTags"].isMember(tag)) - { - return patient_["MainDicomTags"][tag].asCString(); - } - else - { - return defaultValue; - } - } - - void Patient::Delete() - { - Orthanc::HttpClient client(connection_.GetHttpClient()); - client.SetMethod(Orthanc::HttpMethod_Delete); - client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/patients/" + id_); - - std::string s; - if (!client.Apply(s)) - { - throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol); - } - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/Patient.h --- a/OrthancCppClient/Patient.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,121 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "Study.h" - -namespace OrthancClient -{ - /** - * {summary}{Connection to a patient stored in %Orthanc.} - * {description}{This class encapsulates a connection to a patient - * from a remote instance of %Orthanc.} - **/ - class LAAW_API Patient : - public Orthanc::IDynamicObject, - private Orthanc::ArrayFilledByThreads::IFiller - { - private: - const OrthancConnection& connection_; - std::string id_; - Json::Value patient_; - Orthanc::ArrayFilledByThreads studies_; - - void ReadPatient(); - - virtual size_t GetFillerSize() - { - return patient_["Studies"].size(); - } - - virtual Orthanc::IDynamicObject* GetFillerItem(size_t index); - - public: - /** - * {summary}{Create a connection to some patient.} - * {param}{connection The remote instance of %Orthanc.} - * {param}{id The %Orthanc identifier of the patient.} - **/ - Patient(const OrthancConnection& connection, - const char* id); - - /** - * {summary}{Reload the studies of this patient.} - * {description}{This method will reload the list of the studies of this patient. Pay attention to the fact that the studies that have been previously returned by GetStudy() will be invalidated.} - **/ - void Reload() - { - studies_.Reload(); - } - - /** - * {summary}{Return the number of studies for this patient.} - * {returns}{The number of studies.} - **/ - uint32_t GetStudyCount() - { - return studies_.GetSize(); - } - - /** - * {summary}{Get some study of this patient.} - * {description}{This method will return an object that contains information about some study. The studies are indexed by a number between 0 (inclusive) and the result of GetStudyCount() (exclusive).} - * {param}{index The index of the study of interest.} - * {returns}{The study.} - **/ - Study& GetStudy(uint32_t index) - { - return dynamic_cast(studies_.GetItem(index)); - } - - /** - * {summary}{Get the %Orthanc identifier of this patient.} - * {returns}{The identifier.} - **/ - const char* GetId() const - { - return id_.c_str(); - } - - /** - * {summary}{Get the value of one of the main DICOM tags for this patient.} - * {param}{tag The name of the tag of interest ("PatientName", "PatientID", "PatientSex" or "PatientBirthDate").} - * {param}{defaultValue The default value to be returned if this tag does not exist.} - * {returns}{The value of the tag.} - **/ - const char* GetMainDicomTag(const char* tag, - const char* defaultValue) const; - - LAAW_API_INTERNAL void Delete(); - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/Series.cpp --- a/OrthancCppClient/Series.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,527 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../Core/PrecompiledHeaders.h" -#include "Series.h" - -#include "OrthancConnection.h" - -#include -#include - -namespace OrthancClient -{ - namespace - { - class SliceLocator - { - private: - float normal_[3]; - - public: - SliceLocator(Instance& someSlice) - { - /** - * Compute the slice normal from Image Orientation Patient. - * http://nipy.sourceforge.net/nibabel/dicom/dicom_orientation.html#dicom-z-from-slice - * http://dicomiseasy.blogspot.be/2013/06/getting-oriented-using-image-plane.html - * http://www.itk.org/pipermail/insight-users/2003-September/004762.html - **/ - - std::vector cosines; - someSlice.SplitVectorOfFloats(cosines, "ImageOrientationPatient"); // 0020-0037 - - if (cosines.size() != 6) - { - throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat); - } - - normal_[0] = cosines[1] * cosines[5] - cosines[2] * cosines[4]; - normal_[1] = cosines[2] * cosines[3] - cosines[0] * cosines[5]; - normal_[2] = cosines[0] * cosines[4] - cosines[1] * cosines[3]; - } - - - /** - * Compute the distance of some slice along the slice normal. - **/ - float ComputeSliceLocation(Instance& instance) const - { - std::vector ipp; - instance.SplitVectorOfFloats(ipp, "ImagePositionPatient"); // 0020-0032 - if (ipp.size() != 3) - { - throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat); - } - - float dist = 0; - - for (int i = 0; i < 3; i++) - { - dist += normal_[i] * ipp[i]; - } - - return dist; - } - }; - - class ImageDownloadCommand : public Orthanc::ICommand - { - private: - Orthanc::PixelFormat format_; - Orthanc::ImageExtractionMode mode_; - Instance& instance_; - void* target_; - size_t lineStride_; - - public: - ImageDownloadCommand(Instance& instance, - Orthanc::PixelFormat format, - Orthanc::ImageExtractionMode mode, - void* target, - size_t lineStride) : - format_(format), - mode_(mode), - instance_(instance), - target_(target), - lineStride_(lineStride) - { - instance_.SetImageExtractionMode(mode); - } - - virtual bool Execute() - { - using namespace Orthanc; - - unsigned int width = instance_.GetHeight(); - - for (unsigned int y = 0; y < instance_.GetHeight(); y++) - { - uint8_t* p = reinterpret_cast(target_) + y * lineStride_; - - if (instance_.GetPixelFormat() == format_) - { - memcpy(p, instance_.GetBuffer(y), GetBytesPerPixel(instance_.GetPixelFormat()) * instance_.GetWidth()); - } - else if (instance_.GetPixelFormat() == PixelFormat_Grayscale8 && - format_ == PixelFormat_RGB24) - { - const uint8_t* s = reinterpret_cast(instance_.GetBuffer(y)); - for (unsigned int x = 0; x < width; x++, s++, p += 3) - { - p[0] = *s; - p[1] = *s; - p[2] = *s; - } - } - else - { - throw OrthancClientException(ErrorCode_NotImplemented); - } - } - - // Do not keep the image in memory, as we are loading 3D images - instance_.DiscardImage(); - - return true; - } - }; - - - class ProgressToFloatListener : public Orthanc::ThreadedCommandProcessor::IListener - { - private: - float* target_; - - public: - ProgressToFloatListener(float* target) : target_(target) - { - } - - virtual void SignalProgress(unsigned int current, - unsigned int total) - { - if (total == 0) - { - *target_ = 0; - } - else - { - *target_ = static_cast(current) / static_cast(total); - } - } - - virtual void SignalSuccess(unsigned int total) - { - *target_ = 1; - } - - virtual void SignalFailure() - { - *target_ = 0; - } - - virtual void SignalCancel() - { - *target_ = 0; - } - }; - - } - - - void Series::Check3DImage() - { - if (!Is3DImage()) - { - throw OrthancClientException(Orthanc::ErrorCode_NotImplemented); - } - } - - bool Series::Is3DImageInternal() - { - try - { - if (GetInstanceCount() == 0) - { - // Empty image, use some default value (should never happen) - voxelSizeX_ = 1; - voxelSizeY_ = 1; - voxelSizeZ_ = 1; - sliceThickness_ = 1; - - return true; - } - - // Choose a reference slice - Instance& reference = GetInstance(0); - - // Check that all the child instances share the same 3D parameters - for (unsigned int i = 0; i < GetInstanceCount(); i++) - { - Instance& i2 = GetInstance(i); - - if (std::string(reference.GetTagAsString("Columns")) != std::string(i2.GetTagAsString("Columns")) || - std::string(reference.GetTagAsString("Rows")) != std::string(i2.GetTagAsString("Rows")) || - std::string(reference.GetTagAsString("ImageOrientationPatient")) != std::string(i2.GetTagAsString("ImageOrientationPatient")) || - std::string(reference.GetTagAsString("SliceThickness")) != std::string(i2.GetTagAsString("SliceThickness")) || - std::string(reference.GetTagAsString("PixelSpacing")) != std::string(i2.GetTagAsString("PixelSpacing"))) - { - return false; - } - } - - - // Extract X/Y voxel size and slice thickness - std::string s = GetInstance(0).GetTagAsString("PixelSpacing"); // 0028-0030 - size_t pos = s.find('\\'); - assert(pos != std::string::npos); - std::string sy = s.substr(0, pos); - std::string sx = s.substr(pos + 1); - - try - { - voxelSizeX_ = boost::lexical_cast(sx); - voxelSizeY_ = boost::lexical_cast(sy); - } - catch (boost::bad_lexical_cast) - { - throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat); - } - - sliceThickness_ = GetInstance(0).GetTagAsFloat("SliceThickness"); // 0018-0050 - - - // Compute the location of each slice to extract the voxel size along Z - voxelSizeZ_ = std::numeric_limits::infinity(); - - SliceLocator locator(reference); - float referenceSliceLocation = locator.ComputeSliceLocation(reference); - - std::set l; - for (unsigned int i = 0; i < GetInstanceCount(); i++) - { - float location = locator.ComputeSliceLocation(GetInstance(i)); - float distanceToReferenceSlice = fabs(location - referenceSliceLocation); - - l.insert(location); - - if (distanceToReferenceSlice > std::numeric_limits::epsilon() && - distanceToReferenceSlice < voxelSizeZ_) - { - voxelSizeZ_ = distanceToReferenceSlice; - } - } - - - // Make sure that 2 slices do not share the same Z location - return l.size() == GetInstanceCount(); - } - catch (OrthancClientException) - { - return false; - } - } - - void Series::ReadSeries() - { - Orthanc::HttpClient client(connection_.GetHttpClient()); - - client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/series/" + id_); - Json::Value v; - if (!client.Apply(series_)) - { - throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol); - } - } - - Orthanc::IDynamicObject* Series::GetFillerItem(size_t index) - { - Json::Value::ArrayIndex tmp = static_cast(index); - std::string id = series_["Instances"][tmp].asString(); - return new Instance(connection_, id.c_str()); - } - - Series::Series(const OrthancConnection& connection, - const char* id) : - connection_(connection), - id_(id), - instances_(*this) - { - ReadSeries(); - status_ = Status3DImage_NotTested; - url_ = std::string(connection_.GetOrthancUrl()) + "/series/" + id_; - - voxelSizeX_ = 0; - voxelSizeY_ = 0; - voxelSizeZ_ = 0; - sliceThickness_ = 0; - - instances_.SetThreadCount(connection.GetThreadCount()); - } - - - bool Series::Is3DImage() - { - if (status_ == Status3DImage_NotTested) - { - status_ = Is3DImageInternal() ? Status3DImage_True : Status3DImage_False; - } - - return status_ == Status3DImage_True; - } - - unsigned int Series::GetInstanceCount() - { - return instances_.GetSize(); - } - - Instance& Series::GetInstance(unsigned int index) - { - return dynamic_cast(instances_.GetItem(index)); - } - - unsigned int Series::GetWidth() - { - Check3DImage(); - - if (GetInstanceCount() == 0) - return 0; - else - return GetInstance(0).GetTagAsInt("Columns"); - } - - unsigned int Series::GetHeight() - { - Check3DImage(); - - if (GetInstanceCount() == 0) - return 0; - else - return GetInstance(0).GetTagAsInt("Rows"); - } - - const char* Series::GetMainDicomTag(const char* tag, const char* defaultValue) const - { - if (series_["MainDicomTags"].isMember(tag)) - { - return series_["MainDicomTags"][tag].asCString(); - } - else - { - return defaultValue; - } - } - - - - void Series::Load3DImageInternal(void* target, - Orthanc::PixelFormat format, - size_t lineStride, - size_t stackStride, - Orthanc::ThreadedCommandProcessor::IListener* listener) - { - using namespace Orthanc; - - // Choose the extraction mode, depending on the format of the - // target image. - - uint8_t bytesPerPixel; - ImageExtractionMode mode; - - switch (format) - { - case PixelFormat_RGB24: - bytesPerPixel = 3; - mode = ImageExtractionMode_Preview; - break; - - case PixelFormat_Grayscale8: - bytesPerPixel = 1; - mode = ImageExtractionMode_UInt8; // Preview ??? - break; - - case PixelFormat_Grayscale16: - bytesPerPixel = 2; - mode = ImageExtractionMode_UInt16; - break; - - case PixelFormat_SignedGrayscale16: - bytesPerPixel = 2; - mode = ImageExtractionMode_UInt16; - format = PixelFormat_Grayscale16; - break; - - default: - throw OrthancClientException(ErrorCode_NotImplemented); - } - - - // Check that the target image is properly sized - unsigned int sx = GetWidth(); - unsigned int sy = GetHeight(); - - if (lineStride < sx * bytesPerPixel || - stackStride < sx * sy * bytesPerPixel) - { - throw OrthancClientException(ErrorCode_BadRequest); - } - - if (sx == 0 || sy == 0 || GetInstanceCount() == 0) - { - // Empty image, nothing to do - if (listener) - listener->SignalSuccess(0); - return; - } - - - /** - * Order the stacks according to their distance along the slice - * normal (using the "Image Position Patient" tag). This works - * even if the "SliceLocation" tag is absent. - **/ - SliceLocator locator(GetInstance(0)); - - typedef std::map Instances; - Instances instances; - for (unsigned int i = 0; i < GetInstanceCount(); i++) - { - float dist = locator.ComputeSliceLocation(GetInstance(i)); - instances[dist] = &GetInstance(i); - } - - if (instances.size() != GetInstanceCount()) - { - // Several instances have the same Z coordinate - throw OrthancClientException(ErrorCode_NotImplemented); - } - - - // Submit the download of each stack as a set of commands - ThreadedCommandProcessor processor(connection_.GetThreadCount()); - - if (listener != NULL) - { - processor.SetListener(*listener); - } - - uint8_t* stackTarget = reinterpret_cast(target); - for (Instances::iterator it = instances.begin(); it != instances.end(); ++it) - { - processor.Post(new ImageDownloadCommand(*it->second, format, mode, stackTarget, lineStride)); - stackTarget += stackStride; - } - - - // Wait for all the stacks to be downloaded - if (!processor.Join()) - { - throw OrthancClientException(ErrorCode_NetworkProtocol); - } - } - - float Series::GetVoxelSizeX() - { - Check3DImage(); // Is3DImageInternal() will compute the voxel sizes - return voxelSizeX_; - } - - float Series::GetVoxelSizeY() - { - Check3DImage(); // Is3DImageInternal() will compute the voxel sizes - return voxelSizeY_; - } - - float Series::GetVoxelSizeZ() - { - Check3DImage(); // Is3DImageInternal() will compute the voxel sizes - return voxelSizeZ_; - } - - float Series::GetSliceThickness() - { - Check3DImage(); // Is3DImageInternal() will compute the voxel sizes - return sliceThickness_; - } - - void Series::Load3DImage(void* target, - Orthanc::PixelFormat format, - int64_t lineStride, - int64_t stackStride, - float* progress) - { - ProgressToFloatListener listener(progress); - Load3DImageInternal(target, format, static_cast(lineStride), - static_cast(stackStride), &listener); - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/Series.h --- a/OrthancCppClient/Series.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,239 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "Instance.h" - -#include "../Core/MultiThreading/ArrayFilledByThreads.h" -#include "../Core/MultiThreading/ThreadedCommandProcessor.h" - -namespace OrthancClient -{ - /** - * {summary}{Connection to a series stored in %Orthanc.} - * {description}{This class encapsulates a connection to a series - * from a remote instance of %Orthanc.} - **/ - class LAAW_API Series : - public Orthanc::IDynamicObject, - private Orthanc::ArrayFilledByThreads::IFiller - { - private: - enum Status3DImage - { - Status3DImage_NotTested, - Status3DImage_True, - Status3DImage_False - }; - - const OrthancConnection& connection_; - std::string id_, url_; - Json::Value series_; - Orthanc::ArrayFilledByThreads instances_; - Status3DImage status_; - - float voxelSizeX_; - float voxelSizeY_; - float voxelSizeZ_; - float sliceThickness_; - - void Check3DImage(); - - bool Is3DImageInternal(); - - void ReadSeries(); - - virtual size_t GetFillerSize() - { - return series_["Instances"].size(); - } - - virtual Orthanc::IDynamicObject* GetFillerItem(size_t index); - - void Load3DImageInternal(void* target, - Orthanc::PixelFormat format, - size_t lineStride, - size_t stackStride, - Orthanc::ThreadedCommandProcessor::IListener* listener); - - public: - /** - * {summary}{Create a connection to some series.} - * {param}{connection The remote instance of %Orthanc.} - * {param}{id The %Orthanc identifier of the series.} - **/ - Series(const OrthancConnection& connection, - const char* id); - - /** - * {summary}{Reload the instances of this series.} - * {description}{This method will reload the list of the instances of this series. Pay attention to the fact that the instances that have been previously returned by GetInstance() will be invalidated.} - **/ - void Reload() - { - instances_.Reload(); - } - - /** - * {summary}{Return the number of instances for this series.} - * {returns}{The number of instances.} - **/ - uint32_t GetInstanceCount(); - - /** - * {summary}{Get some instance of this series.} - * {description}{This method will return an object that contains information about some instance. The instances are indexed by a number between 0 (inclusive) and the result of GetInstanceCount() (exclusive).} - * {param}{index The index of the instance of interest.} - * {returns}{The instance.} - **/ - Instance& GetInstance(uint32_t index); - - /** - * {summary}{Get the %Orthanc identifier of this series.} - * {returns}{The identifier.} - **/ - const char* GetId() const - { - return id_.c_str(); - } - - /** - * {summary}{Returns the URL to this series.} - * {returns}{The URL.} - **/ - const char* GetUrl() const - { - return url_.c_str(); - } - - - /** - * {summary}{Get the value of one of the main DICOM tags for this series.} - * {param}{tag The name of the tag of interest ("Modality", "Manufacturer", "SeriesDate", "SeriesDescription", "SeriesInstanceUID"...).} - * {param}{defaultValue The default value to be returned if this tag does not exist.} - * {returns}{The value of the tag.} - **/ - const char* GetMainDicomTag(const char* tag, - const char* defaultValue) const; - - /** - * {summary}{Test whether this series encodes a 3D image that can be downloaded from %Orthanc.} - * {returns}{"true" if and only if this is a 3D image.} - **/ - bool Is3DImage(); - - /** - * {summary}{Get the width of the 3D image.} - * {description}{Get the width of the 3D image (i.e. along the X-axis). This call is only valid if this series corresponds to a 3D image.} - * {returns}{The width.} - **/ - uint32_t GetWidth(); - - /** - * {summary}{Get the height of the 3D image.} - * {description}{Get the height of the 3D image (i.e. along the Y-axis). This call is only valid if this series corresponds to a 3D image.} - * {returns}{The height.} - **/ - uint32_t GetHeight(); - - /** - * {summary}{Get the physical size of a voxel along the X-axis.} - * {description}{Get the physical size of a voxel along the X-axis. This call is only valid if this series corresponds to a 3D image.} - * {returns}{The voxel size.} - **/ - float GetVoxelSizeX(); - - /** - * {summary}{Get the physical size of a voxel along the Y-axis.} - * {description}{Get the physical size of a voxel along the Y-axis. This call is only valid if this series corresponds to a 3D image.} - * {returns}{The voxel size.} - **/ - float GetVoxelSizeY(); - - /** - * {summary}{Get the physical size of a voxel along the Z-axis.} - * {description}{Get the physical size of a voxel along the Z-axis. This call is only valid if this series corresponds to a 3D image.} - * {returns}{The voxel size.} - **/ - float GetVoxelSizeZ(); - - /** - * {summary}{Get the slice thickness.} - * {description}{Get the slice thickness. This call is only valid if this series corresponds to a 3D image.} - * {returns}{The slice thickness.} - **/ - float GetSliceThickness(); - - LAAW_API_INTERNAL void Load3DImage(void* target, - Orthanc::PixelFormat format, - int64_t lineStride, - int64_t stackStride, - Orthanc::ThreadedCommandProcessor::IListener& listener) - { - Load3DImageInternal(target, format, static_cast(lineStride), - static_cast(stackStride), &listener); - } - - /** - * {summary}{Load the 3D image into a memory buffer.} - * {description}{Load the 3D image into a memory buffer. This call is only valid if this series corresponds to a 3D image. The "target" buffer must be wide enough to store all the voxels of the image.} - * {param}{target The target memory buffer.} - * {param}{format The memory layout of the voxels.} - * {param}{lineStride The number of bytes between two lines in the target memory buffer.} - * {param}{stackStride The number of bytes between two 2D slices in the target memory buffer.} - **/ - void Load3DImage(void* target, - Orthanc::PixelFormat format, - int64_t lineStride, - int64_t stackStride) - { - Load3DImageInternal(target, format, static_cast(lineStride), - static_cast(stackStride), NULL); - } - - /** - * {summary}{Load the 3D image into a memory buffer.} - * {description}{Load the 3D image into a memory buffer. This call is only valid if this series corresponds to a 3D image. The "target" buffer must be wide enough to store all the voxels of the image. This method will also update a progress indicator to monitor the loading of the image.} - * {param}{target The target memory buffer.} - * {param}{format The memory layout of the voxels.} - * {param}{lineStride The number of bytes between two lines in the target memory buffer.} - * {param}{stackStride The number of bytes between two 2D slices in the target memory buffer.} - * {param}{progress A pointer to a floating-point number that is continuously updated by the download threads to reflect the percentage of completion (between 0 and 1). This value can be read from a separate thread.} - **/ - void Load3DImage(void* target, - Orthanc::PixelFormat format, - int64_t lineStride, - int64_t stackStride, - float* progress); - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/AUTOGENERATED/ExternC.cpp --- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/ExternC.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1525 +0,0 @@ -/** - * Laaw - Lightweight, Automated API Wrapper - * Copyright (C) 2010-2013 Jomago - Alain Mazy, Benjamin Golinvaux, - * Sebastien Jodogne - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include -#include // For strcpy() and strlen() -#include // For free() - -static char* LAAW_EXTERNC_CopyString(const char* str) -{ - char* copy = reinterpret_cast(malloc(strlen(str) + 1)); - strcpy(copy, str); - return copy; -} - -extern "C" -{ - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_1f1acb322ea4d0aad65172824607673c(void** newObject, const char* arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - *newObject = new OrthancClient::OrthancConnection(reinterpret_cast< const char* >(arg0)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_f3fd272e4636f6a531aabb72ee01cd5b(void** newObject, const char* arg0, const char* arg1, const char* arg2) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - *newObject = new OrthancClient::OrthancConnection(reinterpret_cast< const char* >(arg0), reinterpret_cast< const char* >(arg1), reinterpret_cast< const char* >(arg2)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_12d3de0a96e9efb11136a9811bb9ed38(void* thisObject) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - delete static_cast(thisObject); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_557aee7b61817292a0f31269d3c35db7(const void* thisObject, uint32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::OrthancConnection* this_ = static_cast(thisObject); -*result = this_->GetThreadCount(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_0b8dff0ce67f10954a49b059e348837e(void* thisObject, uint32_t arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::OrthancConnection* this_ = static_cast(thisObject); -this_->SetThreadCount(arg0); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_e05097c153f676e5a5ee54dcfc78256f(void* thisObject) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::OrthancConnection* this_ = static_cast(thisObject); -this_->Reload(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_e840242bf58d17d3c1d722da09ce88e0(const void* thisObject, const char** result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::OrthancConnection* this_ = static_cast(thisObject); -*result = this_->GetOrthancUrl(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_c9af31433001b5dfc012a552dc6d0050(void* thisObject, uint32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::OrthancConnection* this_ = static_cast(thisObject); -*result = this_->GetPatientCount(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_3fba4d6b818180a44cd1cae6046334dc(void* thisObject, void** result, uint32_t arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::OrthancConnection* this_ = static_cast(thisObject); -*result = &this_->GetPatient(arg0); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_aeb20dc75b9246188db857317e5e0ce7(void* thisObject, uint32_t arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::OrthancConnection* this_ = static_cast(thisObject); -this_->DeletePatient(arg0); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_62689803d9871e4d9c51a648640b320b(void* thisObject, const char* arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::OrthancConnection* this_ = static_cast(thisObject); -this_->StoreFile(reinterpret_cast< const char* >(arg0)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_2fb64c9e5a67eccd413b0e913469a421(void* thisObject, const void* arg0, uint64_t arg1) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::OrthancConnection* this_ = static_cast(thisObject); -this_->Store(reinterpret_cast< const void* >(arg0), arg1); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_6cf0d7268667f9b0aa4511bacf184919(void** newObject, void* arg0, const char* arg1) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - *newObject = new OrthancClient::Patient(*reinterpret_cast< ::OrthancClient::OrthancConnection* >(arg0), reinterpret_cast< const char* >(arg1)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_7d81cd502ee27e859735d0ea7112b5a1(void* thisObject) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - delete static_cast(thisObject); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_f756172daf04516eec3a566adabb4335(void* thisObject) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Patient* this_ = static_cast(thisObject); -this_->Reload(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_ddb68763ec902a97d579666a73a20118(void* thisObject, uint32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Patient* this_ = static_cast(thisObject); -*result = this_->GetStudyCount(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_fba3c68b4be7558dbc65f7ce1ab57d63(void* thisObject, void** result, uint32_t arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Patient* this_ = static_cast(thisObject); -*result = &this_->GetStudy(arg0); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_b4ca99d958f843493e58d1ef967340e1(const void* thisObject, const char** result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Patient* this_ = static_cast(thisObject); -*result = this_->GetId(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_78d5cc76d282437b6f93ec3b82c35701(const void* thisObject, const char** result, const char* arg0, const char* arg1) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Patient* this_ = static_cast(thisObject); -*result = this_->GetMainDicomTag(reinterpret_cast< const char* >(arg0), reinterpret_cast< const char* >(arg1)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_193599b9e345384fcdfcd47c29c55342(void** newObject, void* arg0, const char* arg1) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - *newObject = new OrthancClient::Series(*reinterpret_cast< ::OrthancClient::OrthancConnection* >(arg0), reinterpret_cast< const char* >(arg1)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_7c97f17063a357d38c5fab1136ad12a0(void* thisObject) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - delete static_cast(thisObject); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_48a2a1a9d68c047e22bfba23014643d2(void* thisObject) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Series* this_ = static_cast(thisObject); -this_->Reload(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_852bf8296ca21c5fde5ec565cc10721d(void* thisObject, uint32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Series* this_ = static_cast(thisObject); -*result = this_->GetInstanceCount(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_efd04574e0779faa83df1f2d8f9888db(void* thisObject, void** result, uint32_t arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Series* this_ = static_cast(thisObject); -*result = &this_->GetInstance(arg0); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_736247ff5e8036dac38163da6f666ed5(const void* thisObject, const char** result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Series* this_ = static_cast(thisObject); -*result = this_->GetId(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_d82d2598a7a73f4b6fcc0c09c25b08ca(const void* thisObject, const char** result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Series* this_ = static_cast(thisObject); -*result = this_->GetUrl(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_88134b978f9acb2aecdadf54aeab3c64(const void* thisObject, const char** result, const char* arg0, const char* arg1) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Series* this_ = static_cast(thisObject); -*result = this_->GetMainDicomTag(reinterpret_cast< const char* >(arg0), reinterpret_cast< const char* >(arg1)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_152cb1b704c053d24b0dab7461ba6ea3(void* thisObject, int32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Series* this_ = static_cast(thisObject); -*result = this_->Is3DImage(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_eee03f337ec81d9f1783cd41e5238757(void* thisObject, uint32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Series* this_ = static_cast(thisObject); -*result = this_->GetWidth(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_006f08237bd7611636fc721baebfb4c5(void* thisObject, uint32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Series* this_ = static_cast(thisObject); -*result = this_->GetHeight(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_b794f5cd3dad7d7b575dd1fd902afdd0(void* thisObject, float* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Series* this_ = static_cast(thisObject); -*result = this_->GetVoxelSizeX(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_8ee2e50dd9df8f66a3c1766090dd03ab(void* thisObject, float* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Series* this_ = static_cast(thisObject); -*result = this_->GetVoxelSizeY(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_046aed35bbe4751691f4c34cc249a61d(void* thisObject, float* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Series* this_ = static_cast(thisObject); -*result = this_->GetVoxelSizeZ(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_2be452e7af5bf7dfd8c5021842674497(void* thisObject, float* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Series* this_ = static_cast(thisObject); -*result = this_->GetSliceThickness(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_4dcc7a0fd025efba251ac6e9b701c2c5(void* thisObject, void* arg0, int32_t arg1, int64_t arg2, int64_t arg3) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Series* this_ = static_cast(thisObject); -this_->Load3DImage(reinterpret_cast< void* >(arg0), static_cast< ::Orthanc::PixelFormat >(arg1), arg2, arg3); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_b2601a161c24ad0a1d3586246f87452c(void* thisObject, void* arg0, int32_t arg1, int64_t arg2, int64_t arg3, float* arg4) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Series* this_ = static_cast(thisObject); -this_->Load3DImage(reinterpret_cast< void* >(arg0), static_cast< ::Orthanc::PixelFormat >(arg1), arg2, arg3, reinterpret_cast< float* >(arg4)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_b01c6003238eb46c8db5dc823d7ca678(void** newObject, void* arg0, const char* arg1) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - *newObject = new OrthancClient::Study(*reinterpret_cast< ::OrthancClient::OrthancConnection* >(arg0), reinterpret_cast< const char* >(arg1)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_0147007fb99bad8cd95a139ec8795376(void* thisObject) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - delete static_cast(thisObject); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_e65b20b7e0170b67544cd6664a4639b7(void* thisObject) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Study* this_ = static_cast(thisObject); -this_->Reload(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_470e981b0e41f17231ba0ae6f3033321(void* thisObject, uint32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Study* this_ = static_cast(thisObject); -*result = this_->GetSeriesCount(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_04cefd138b6ea15ad909858f2a0a8f05(void* thisObject, void** result, uint32_t arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Study* this_ = static_cast(thisObject); -*result = &this_->GetSeries(arg0); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_aee5b1f6f0c082f2c3b0986f9f6a18c7(const void* thisObject, const char** result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Study* this_ = static_cast(thisObject); -*result = this_->GetId(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_93965682bace75491413e1f0b8d5a654(const void* thisObject, const char** result, const char* arg0, const char* arg1) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Study* this_ = static_cast(thisObject); -*result = this_->GetMainDicomTag(reinterpret_cast< const char* >(arg0), reinterpret_cast< const char* >(arg1)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_6c5ad02f91b583e29cebd0bd319ce21d(void** newObject, void* arg0, const char* arg1) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - *newObject = new OrthancClient::Instance(*reinterpret_cast< ::OrthancClient::OrthancConnection* >(arg0), reinterpret_cast< const char* >(arg1)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_4068241c44a9c1367fe0e57be523f207(void* thisObject) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - delete static_cast(thisObject); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_236ee8b403bc99535a8a4695c0cd45cb(const void* thisObject, const char** result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetId(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_2a437b7aba6bb01e81113835be8f0146(void* thisObject, int32_t arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Instance* this_ = static_cast(thisObject); -this_->SetImageExtractionMode(static_cast< ::Orthanc::ImageExtractionMode >(arg0)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_2bcbcb850934ae0bb4c6f0cc940e6cda(const void* thisObject, int32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetImageExtractionMode(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_8d415c3a78a48e7e61d9fd24e7c79484(const void* thisObject, const char** result, const char* arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetTagAsString(reinterpret_cast< const char* >(arg0)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_70d2f8398bbc63b5f792b69b4ad5fecb(const void* thisObject, float* result, const char* arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetTagAsFloat(reinterpret_cast< const char* >(arg0)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_1729a067d902771517388eedd7346b23(const void* thisObject, int32_t* result, const char* arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetTagAsInt(reinterpret_cast< const char* >(arg0)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_72e2aeee66cd3abd8ab7e987321c3745(void* thisObject, uint32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetWidth(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_1ea3df5a1ac1a1a687fe7325adddb6f0(void* thisObject, uint32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetHeight(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_99b4f370e4f532d8b763e2cb49db92f8(void* thisObject, uint32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetPitch(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_c41c742b68617f1c0590577a0a5ebc0c(void* thisObject, int32_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetPixelFormat(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_142dd2feba0fc1d262bbd0baeb441a8b(void* thisObject, const void** result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetBuffer(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_5f5c9f81a4dff8daa6c359f1d0488fef(void* thisObject, const void** result, uint32_t arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetBuffer(arg0); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_9ca979fffd08fa256306d4e68d8b0e91(void* thisObject, uint64_t* result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetDicomSize(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_6f2d77a26edc91c28d89408dbc3c271e(void* thisObject, const void** result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetDicom(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_c0f494b80d4ff8b232df7a75baa0700a(void* thisObject) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Instance* this_ = static_cast(thisObject); -this_->DiscardImage(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_d604f44bd5195e082e745e9cbc164f4c(void* thisObject) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Instance* this_ = static_cast(thisObject); -this_->DiscardDicom(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_1710299d1c5f3b1f2b7cf3962deebbfd(void* thisObject, const char* arg0) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - OrthancClient::Instance* this_ = static_cast(thisObject); -this_->LoadTagContent(reinterpret_cast< const char* >(arg0)); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_bb55aaf772ddceaadee36f4e54136bcb(const void* thisObject, const char** result) - { - try - { - #ifdef LAAW_EXTERNC_START_FUNCTION - LAAW_EXTERNC_START_FUNCTION; - #endif - - const OrthancClient::Instance* this_ = static_cast(thisObject); -*result = this_->GetLoadedTagContent(); - - return NULL; - } - catch (::Laaw::LaawException& e) - { - return LAAW_EXTERNC_CopyString(e.What()); - } - catch (...) - { - return LAAW_EXTERNC_CopyString("..."); - } - } - - - LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetDescription() - { - return "Native client to the REST API of Orthanc"; - } - - LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetCompany() - { - return "University Hospital of Liege"; - } - - LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetProduct() - { - return "OrthancClient"; - } - - LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetCopyright() - { - return "(c) 2012-2015, Sebastien Jodogne, University Hospital of Liege"; - } - - LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetVersion() - { - return "0.8"; - } - - LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetFileVersion() - { - return "0.8.0.5"; - } - - LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetFullVersion() - { - return "0.8.5"; - } - - LAAW_EXPORT_DLL_API void LAAW_CALL_CONVENTION LAAW_EXTERNC_FreeString(char* str) - { - if (str != NULL) - free(str); - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h --- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1817 +0,0 @@ -/** - * Laaw - Lightweight, Automated API Wrapper - * Copyright (C) 2010-2013 Jomago - Alain Mazy, Benjamin Golinvaux, - * Sebastien Jodogne - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -/** - * @file - **/ - -#pragma once - -#include -#include -#include -#include - -#if defined(_WIN32) - -/******************************************************************** - ** This is the Windows-specific section - ********************************************************************/ - -#include - -/* cf. http://sourceforge.net/p/predef/wiki/Architectures/ */ -#ifdef _M_X64 -/* 64 bits target */ -#define LAAW_ORTHANC_CLIENT_CALL_CONV __fastcall -#define LAAW_ORTHANC_CLIENT_CALL_DECORATION(Name, StdCallSuffix) Name -#define LAAW_ORTHANC_CLIENT_DEFAULT_PATH "OrthancClient_Windows64.dll" -#else -/* 32 bits target */ -#define LAAW_ORTHANC_CLIENT_CALL_CONV __stdcall -#define LAAW_ORTHANC_CLIENT_CALL_DECORATION(Name, StdCallSuffix) "_" Name "@" StdCallSuffix -#define LAAW_ORTHANC_CLIENT_DEFAULT_PATH "OrthancClient_Windows32.dll" -#endif - -#define LAAW_ORTHANC_CLIENT_HANDLE_TYPE HINSTANCE -#define LAAW_ORTHANC_CLIENT_HANDLE_NULL 0 -#define LAAW_ORTHANC_CLIENT_FUNCTION_TYPE FARPROC -#define LAAW_ORTHANC_CLIENT_LOADER(path) LoadLibraryA(path) -#define LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle, name, decoration) GetProcAddress(handle, LAAW_ORTHANC_CLIENT_CALL_DECORATION(name, decoration)) -#define LAAW_ORTHANC_CLIENT_CLOSER(handle) FreeLibrary(handle) - - -/******************************************************************** - ** This is the Linux-specific section - ********************************************************************/ - -#elif defined (__linux) - -#include -#include - -/* cf. http://sourceforge.net/p/predef/wiki/Architectures/ */ -#ifdef __amd64__ -#define LAAW_ORTHANC_CLIENT_DEFAULT_PATH "libOrthancClient.so.0.8" -#else -#define LAAW_ORTHANC_CLIENT_DEFAULT_PATH "libOrthancClient.so.0.8" -#endif - -#define LAAW_ORTHANC_CLIENT_CALL_CONV -#define LAAW_ORTHANC_CLIENT_HANDLE_TYPE void* -#define LAAW_ORTHANC_CLIENT_HANDLE_NULL NULL -#define LAAW_ORTHANC_CLIENT_FUNCTION_TYPE intptr_t -#define LAAW_ORTHANC_CLIENT_LOADER(path) dlopen(path, RTLD_LAZY) -#define LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle, name, decoration) (LAAW_ORTHANC_CLIENT_FUNCTION_TYPE) dlsym(handle, name) -#define LAAW_ORTHANC_CLIENT_CLOSER(handle) dlclose(handle) - - -#else -#error Please support your platform here -#endif - - -/******************************************************************** - ** Definition of the integer types - ********************************************************************/ - -#ifndef LAAW_INT8 // Only define the integer types once - -#if defined(__GNUC__) - -// Under GCC (including MinGW), the stdint.h standard header is used. - -#include - -#define LAAW_INT8 int8_t -#define LAAW_UINT8 uint8_t -#define LAAW_INT16 int16_t -#define LAAW_UINT16 uint16_t -#define LAAW_INT32 int32_t -#define LAAW_UINT32 uint32_t -#define LAAW_INT64 int64_t -#define LAAW_UINT64 uint64_t - -#elif defined(_MSC_VER) - -// Under Visual Studio, it is required to define the various integer -// types by hand. - -#if (_MSC_VER < 1300) -typedef signed char LAAW_INT8; -typedef signed short LAAW_INT16; -typedef signed int LAAW_INT32; -typedef unsigned char LAAW_UINT8; -typedef unsigned short LAAW_UINT16; -typedef unsigned int LAAW_UINT32; -#else -typedef signed __int8 LAAW_INT8; -typedef signed __int16 LAAW_INT16; -typedef signed __int32 LAAW_INT32; -typedef unsigned __int8 LAAW_UINT8; -typedef unsigned __int16 LAAW_UINT16; -typedef unsigned __int32 LAAW_UINT32; -#endif - -typedef signed __int64 LAAW_INT64; -typedef unsigned __int64 LAAW_UINT64; - -#else -#error "Please support your compiler here" -#endif - -#endif - - - - - -/******************************************************************** - ** This is a shared section between Windows and Linux - ********************************************************************/ - -namespace OrthancClient { -/** - * @brief Exception class that is thrown by the functions of this shared library. - **/ -class OrthancClientException : public std::exception - { - private: - std::string message_; - - public: - /** - * @brief Constructs an exception. - * @param message The error message. - **/ - OrthancClientException(std::string message) : message_(message) - { - } - - ~OrthancClientException() throw() - { - } - - /** - * @brief Get the error message associated with this exception. - * @returns The error message. - **/ - const std::string& What() const throw() - { - return message_; - } -}; -} - - -namespace OrthancClient { namespace Internals { -/** - * This internal class implements a Singleton design pattern that will - * store a reference to the shared library handle, together with a - * pointer to each function in the shared library. - **/ -class Library - { - private: - LAAW_ORTHANC_CLIENT_HANDLE_TYPE handle_; - LAAW_ORTHANC_CLIENT_FUNCTION_TYPE functionsIndex_[63 + 1]; - - - - void Load(const char* sharedLibraryPath) - { - - if (handle_ != LAAW_ORTHANC_CLIENT_HANDLE_NULL) - { - // Do nothing if the library is already loaded - return; - } - - /* Setup the path to the default shared library if not provided */ - if (sharedLibraryPath == NULL) - { - sharedLibraryPath = LAAW_ORTHANC_CLIENT_DEFAULT_PATH; - } - - /* Load the shared library */ - handle_ = LAAW_ORTHANC_CLIENT_LOADER(sharedLibraryPath); - - - if (handle_ == LAAW_ORTHANC_CLIENT_HANDLE_NULL) - { - throw ::OrthancClient::OrthancClientException("Error loading shared library"); - } - - LoadFunctions(); - } - - inline void LoadFunctions(); - - void FreeString(char* str) - { - typedef void (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (char*); - Function function = (Function) GetFunction(63); - function(str); - } - - Library() - { - handle_ = LAAW_ORTHANC_CLIENT_HANDLE_NULL; - } - - ~Library() - { - Finalize(); - } - - public: - LAAW_ORTHANC_CLIENT_FUNCTION_TYPE GetFunction(unsigned int index) - { - /** - * If the library has not been manually initialized by a call to - * ::OrthancClient::Initialize(), it is loaded from - * the default location (lazy initialization). - **/ - if (handle_ == NULL) - { - Load(NULL); - } - - return functionsIndex_[index]; - } - - void ThrowExceptionIfNeeded(char* message) - { - if (message != NULL) - { - std::string tmp(message); - FreeString(message); - throw ::OrthancClient::OrthancClientException(tmp); - } - } - - static inline Library& GetInstance() - { - /** - * This function defines a "static variable" inside a "static - * inline method" of a class. This ensures that a single - * instance of this variable will be used across all the - * compilation modules of the software. - * http://stackoverflow.com/a/1389403/881731 - **/ - - static Library singleton; - return singleton; - } - - static void Initialize(const char* sharedLibraryPath) - { - GetInstance().Load(sharedLibraryPath); - } - - void Finalize() - { - if (handle_ != LAAW_ORTHANC_CLIENT_HANDLE_NULL) - { -#if 0 - /** - * Do not explicitly unload the shared library, as it might - * interfere with the destruction of static objects declared - * inside the library (e.g. this is the case of gflags that is - * internally used by googlelog). - **/ - LAAW_ORTHANC_CLIENT_CLOSER(handle_); -#endif - handle_ = LAAW_ORTHANC_CLIENT_HANDLE_NULL; - } - } -}; -}} - - -/*! - * \addtogroup Global Global definitions. - * @{ - * @} - */ - - -namespace OrthancClient { -/*! - * \addtogroup Initialization Initialization of the shared library. - * @{ - */ - -/** - * @brief Manually initialize the shared library, using the default library name. - * - * Call this method before using the library to ensure correct - * behaviour in multi-threaded applications. This method is also - * useful to control the time at which the shared library is - * loaded (e.g. for real-time applications). - **/ -inline void Initialize() -{ - ::OrthancClient::Internals::Library::Initialize(NULL); -} - -/** - * @brief Manually initialize the shared library. - * - * Call this method before using the library to ensure correct - * behaviour in multi-threaded applications. This method is also - * useful to control the time at which the shared library is - * loaded (e.g. for real-time applications). - * - * @param sharedLibraryPath The path to the shared library that - * contains the module. - **/ -inline void Initialize(const std::string& sharedLibraryPath) -{ - ::OrthancClient::Internals::Library::Initialize(sharedLibraryPath.c_str()); -} - -/** - * @brief Manually finalize the shared library. - * - * Calling explicitly this function is not mandatory. It is useful to - * force the release of the resources acquired by the shared library, - * or to manually control the order in which the global variables get - * deleted. - **/ -inline void Finalize() -{ - ::OrthancClient::Internals::Library::GetInstance().Finalize(); -} - - -/** - * @} - */ -} - - -namespace OrthancClient { namespace Internals { -inline void Library::LoadFunctions() -{ - typedef const char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (); - Function getVersion = (Function) LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_GetVersion", "0"); - if (getVersion == NULL) - { - throw ::OrthancClient::OrthancClientException("Unable to get the library version"); - } - - /** - * It is assumed that the API does not change when the revision - * number (MAJOR.MINOR.REVISION) changes. - **/ - if (strcmp(getVersion(), "0.8")) - { - throw ::OrthancClient::OrthancClientException("Mismatch between the C++ header and the library version"); - } - - functionsIndex_[63] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_FreeString", "4"); - functionsIndex_[3] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_557aee7b61817292a0f31269d3c35db7", "8"); - functionsIndex_[4] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_0b8dff0ce67f10954a49b059e348837e", "8"); - functionsIndex_[5] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_e05097c153f676e5a5ee54dcfc78256f", "4"); - functionsIndex_[6] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_e840242bf58d17d3c1d722da09ce88e0", "8"); - functionsIndex_[7] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_c9af31433001b5dfc012a552dc6d0050", "8"); - functionsIndex_[8] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_3fba4d6b818180a44cd1cae6046334dc", "12"); - functionsIndex_[9] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_aeb20dc75b9246188db857317e5e0ce7", "8"); - functionsIndex_[10] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_62689803d9871e4d9c51a648640b320b", "8"); - functionsIndex_[11] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_2fb64c9e5a67eccd413b0e913469a421", "16"); - functionsIndex_[0] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_1f1acb322ea4d0aad65172824607673c", "8"); - functionsIndex_[1] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_f3fd272e4636f6a531aabb72ee01cd5b", "16"); - functionsIndex_[2] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_12d3de0a96e9efb11136a9811bb9ed38", "4"); - functionsIndex_[14] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_f756172daf04516eec3a566adabb4335", "4"); - functionsIndex_[15] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_ddb68763ec902a97d579666a73a20118", "8"); - functionsIndex_[16] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_fba3c68b4be7558dbc65f7ce1ab57d63", "12"); - functionsIndex_[17] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_b4ca99d958f843493e58d1ef967340e1", "8"); - functionsIndex_[18] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_78d5cc76d282437b6f93ec3b82c35701", "16"); - functionsIndex_[12] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_6cf0d7268667f9b0aa4511bacf184919", "12"); - functionsIndex_[13] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_7d81cd502ee27e859735d0ea7112b5a1", "4"); - functionsIndex_[21] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_48a2a1a9d68c047e22bfba23014643d2", "4"); - functionsIndex_[22] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_852bf8296ca21c5fde5ec565cc10721d", "8"); - functionsIndex_[23] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_efd04574e0779faa83df1f2d8f9888db", "12"); - functionsIndex_[24] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_736247ff5e8036dac38163da6f666ed5", "8"); - functionsIndex_[25] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_d82d2598a7a73f4b6fcc0c09c25b08ca", "8"); - functionsIndex_[26] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_88134b978f9acb2aecdadf54aeab3c64", "16"); - functionsIndex_[27] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_152cb1b704c053d24b0dab7461ba6ea3", "8"); - functionsIndex_[28] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_eee03f337ec81d9f1783cd41e5238757", "8"); - functionsIndex_[29] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_006f08237bd7611636fc721baebfb4c5", "8"); - functionsIndex_[30] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_b794f5cd3dad7d7b575dd1fd902afdd0", "8"); - functionsIndex_[31] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_8ee2e50dd9df8f66a3c1766090dd03ab", "8"); - functionsIndex_[32] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_046aed35bbe4751691f4c34cc249a61d", "8"); - functionsIndex_[33] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_2be452e7af5bf7dfd8c5021842674497", "8"); - functionsIndex_[34] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_4dcc7a0fd025efba251ac6e9b701c2c5", "28"); - functionsIndex_[35] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_b2601a161c24ad0a1d3586246f87452c", "32"); - functionsIndex_[19] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_193599b9e345384fcdfcd47c29c55342", "12"); - functionsIndex_[20] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_7c97f17063a357d38c5fab1136ad12a0", "4"); - functionsIndex_[38] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_e65b20b7e0170b67544cd6664a4639b7", "4"); - functionsIndex_[39] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_470e981b0e41f17231ba0ae6f3033321", "8"); - functionsIndex_[40] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_04cefd138b6ea15ad909858f2a0a8f05", "12"); - functionsIndex_[41] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_aee5b1f6f0c082f2c3b0986f9f6a18c7", "8"); - functionsIndex_[42] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_93965682bace75491413e1f0b8d5a654", "16"); - functionsIndex_[36] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_b01c6003238eb46c8db5dc823d7ca678", "12"); - functionsIndex_[37] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_0147007fb99bad8cd95a139ec8795376", "4"); - functionsIndex_[45] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_236ee8b403bc99535a8a4695c0cd45cb", "8"); - functionsIndex_[46] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_2a437b7aba6bb01e81113835be8f0146", "8"); - functionsIndex_[47] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_2bcbcb850934ae0bb4c6f0cc940e6cda", "8"); - functionsIndex_[48] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_8d415c3a78a48e7e61d9fd24e7c79484", "12"); - functionsIndex_[49] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_70d2f8398bbc63b5f792b69b4ad5fecb", "12"); - functionsIndex_[50] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_1729a067d902771517388eedd7346b23", "12"); - functionsIndex_[51] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_72e2aeee66cd3abd8ab7e987321c3745", "8"); - functionsIndex_[52] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_1ea3df5a1ac1a1a687fe7325adddb6f0", "8"); - functionsIndex_[53] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_99b4f370e4f532d8b763e2cb49db92f8", "8"); - functionsIndex_[54] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_c41c742b68617f1c0590577a0a5ebc0c", "8"); - functionsIndex_[55] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_142dd2feba0fc1d262bbd0baeb441a8b", "8"); - functionsIndex_[56] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_5f5c9f81a4dff8daa6c359f1d0488fef", "12"); - functionsIndex_[57] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_9ca979fffd08fa256306d4e68d8b0e91", "8"); - functionsIndex_[58] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_6f2d77a26edc91c28d89408dbc3c271e", "8"); - functionsIndex_[59] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_c0f494b80d4ff8b232df7a75baa0700a", "4"); - functionsIndex_[60] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_d604f44bd5195e082e745e9cbc164f4c", "4"); - functionsIndex_[61] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_1710299d1c5f3b1f2b7cf3962deebbfd", "8"); - functionsIndex_[62] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_bb55aaf772ddceaadee36f4e54136bcb", "8"); - functionsIndex_[43] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_6c5ad02f91b583e29cebd0bd319ce21d", "12"); - functionsIndex_[44] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_4068241c44a9c1367fe0e57be523f207", "4"); - - /* Check whether the functions were properly loaded */ - for (unsigned int i = 0; i <= 63; i++) - { - if (functionsIndex_[i] == (LAAW_ORTHANC_CLIENT_FUNCTION_TYPE) NULL) - { - throw ::OrthancClient::OrthancClientException("Unable to load the functions of the shared library"); - } - } -} -}} -namespace OrthancClient -{ - class OrthancConnection; -} - -namespace OrthancClient -{ - class Patient; -} - -namespace OrthancClient -{ - class Series; -} - -namespace OrthancClient -{ - class Study; -} - -namespace OrthancClient -{ - class Instance; -} - -namespace Orthanc -{ - /** - * @brief The memory layout of the pixels (resp. voxels) of a 2D (resp. 3D) image. - * - * The memory layout of the pixels (resp. voxels) of a 2D (resp. 3D) image. - * - * @ingroup Global - **/ - enum PixelFormat - { - /** - * @brief Graylevel, signed 16bpp image. - * - * The image is graylevel. Each pixel is signed and stored in two bytes. - * - **/ - PixelFormat_SignedGrayscale16 = 5, - /** - * @brief Color image in RGB24 format. - * - * This format describes a color image. The pixels are stored in 3 consecutive bytes. The memory layout is RGB. - * - **/ - PixelFormat_RGB24 = 1, - /** - * @brief Color image in RGBA32 format. - * - * This format describes a color image. The pixels are stored in 4 consecutive bytes. The memory layout is RGBA. - * - **/ - PixelFormat_RGBA32 = 2, - /** - * @brief Graylevel 8bpp image. - * - * The image is graylevel. Each pixel is unsigned and stored in one byte. - * - **/ - PixelFormat_Grayscale8 = 3, - /** - * @brief Graylevel, unsigned 16bpp image. - * - * The image is graylevel. Each pixel is unsigned and stored in two bytes. - * - **/ - PixelFormat_Grayscale16 = 4 - }; -} - -namespace Orthanc -{ - /** - * @brief The extraction mode specifies the way the values of the pixels are scaled when downloading a 2D image. - * - * The extraction mode specifies the way the values of the pixels are scaled when downloading a 2D image. - * - * @ingroup Global - **/ - enum ImageExtractionMode - { - /** - * @brief Truncation to the [-32768, 32767] range. - * - * Truncation to the [-32768, 32767] range. - * - **/ - ImageExtractionMode_Int16 = 4, - /** - * @brief Rescaled to 8bpp. - * - * The minimum value of the image is set to 0, and its maximum value is set to 255. - * - **/ - ImageExtractionMode_Preview = 1, - /** - * @brief Truncation to the [0, 255] range. - * - * Truncation to the [0, 255] range. - * - **/ - ImageExtractionMode_UInt8 = 2, - /** - * @brief Truncation to the [0, 65535] range. - * - * Truncation to the [0, 65535] range. - * - **/ - ImageExtractionMode_UInt16 = 3 - }; -} - -namespace OrthancClient -{ - /** - * @brief Connection to an instance of %Orthanc. - * - * This class encapsulates a connection to a remote instance of %Orthanc through its REST API. - * - **/ - class OrthancConnection - { - friend class ::OrthancClient::Patient; - friend class ::OrthancClient::Series; - friend class ::OrthancClient::Study; - friend class ::OrthancClient::Instance; - private: - bool isReference_; - OrthancConnection& operator= (const OrthancConnection&); // Assignment is forbidden - void* pimpl_; - OrthancConnection(void* pimpl) : isReference_(true), pimpl_(pimpl) {} - public: - /** - * @brief Construct a new reference to this object. - * - * Construct a new reference to this object. Pay attention to the fact that when the referenced object is deleted, the content of this object will be invalid. - * - * @param other The original object. - **/ - OrthancConnection(const OrthancConnection& other) : isReference_(true), pimpl_(other.pimpl_) { } - inline OrthancConnection(const ::std::string& orthancUrl); - inline OrthancConnection(const ::std::string& orthancUrl, const ::std::string& username, const ::std::string& password); - inline ~OrthancConnection(); - inline LAAW_UINT32 GetThreadCount() const; - inline void SetThreadCount(LAAW_UINT32 threadCount); - inline void Reload(); - inline ::std::string GetOrthancUrl() const; - inline LAAW_UINT32 GetPatientCount(); - inline ::OrthancClient::Patient GetPatient(LAAW_UINT32 index); - inline void DeletePatient(LAAW_UINT32 index); - inline void StoreFile(const ::std::string& filename); - inline void Store(const void* dicom, LAAW_UINT64 size); - }; -} - -namespace OrthancClient -{ - /** - * @brief Connection to a patient stored in %Orthanc. - * - * This class encapsulates a connection to a patient from a remote instance of %Orthanc. - * - **/ - class Patient - { - friend class ::OrthancClient::OrthancConnection; - friend class ::OrthancClient::Series; - friend class ::OrthancClient::Study; - friend class ::OrthancClient::Instance; - private: - bool isReference_; - Patient& operator= (const Patient&); // Assignment is forbidden - void* pimpl_; - Patient(void* pimpl) : isReference_(true), pimpl_(pimpl) {} - public: - /** - * @brief Construct a new reference to this object. - * - * Construct a new reference to this object. Pay attention to the fact that when the referenced object is deleted, the content of this object will be invalid. - * - * @param other The original object. - **/ - Patient(const Patient& other) : isReference_(true), pimpl_(other.pimpl_) { } - inline Patient(::OrthancClient::OrthancConnection& connection, const ::std::string& id); - inline ~Patient(); - inline void Reload(); - inline LAAW_UINT32 GetStudyCount(); - inline ::OrthancClient::Study GetStudy(LAAW_UINT32 index); - inline ::std::string GetId() const; - inline ::std::string GetMainDicomTag(const ::std::string& tag, const ::std::string& defaultValue) const; - }; -} - -namespace OrthancClient -{ - /** - * @brief Connection to a series stored in %Orthanc. - * - * This class encapsulates a connection to a series from a remote instance of %Orthanc. - * - **/ - class Series - { - friend class ::OrthancClient::OrthancConnection; - friend class ::OrthancClient::Patient; - friend class ::OrthancClient::Study; - friend class ::OrthancClient::Instance; - private: - bool isReference_; - Series& operator= (const Series&); // Assignment is forbidden - void* pimpl_; - Series(void* pimpl) : isReference_(true), pimpl_(pimpl) {} - public: - /** - * @brief Construct a new reference to this object. - * - * Construct a new reference to this object. Pay attention to the fact that when the referenced object is deleted, the content of this object will be invalid. - * - * @param other The original object. - **/ - Series(const Series& other) : isReference_(true), pimpl_(other.pimpl_) { } - inline Series(::OrthancClient::OrthancConnection& connection, const ::std::string& id); - inline ~Series(); - inline void Reload(); - inline LAAW_UINT32 GetInstanceCount(); - inline ::OrthancClient::Instance GetInstance(LAAW_UINT32 index); - inline ::std::string GetId() const; - inline ::std::string GetUrl() const; - inline ::std::string GetMainDicomTag(const ::std::string& tag, const ::std::string& defaultValue) const; - inline bool Is3DImage(); - inline LAAW_UINT32 GetWidth(); - inline LAAW_UINT32 GetHeight(); - inline float GetVoxelSizeX(); - inline float GetVoxelSizeY(); - inline float GetVoxelSizeZ(); - inline float GetSliceThickness(); - inline void Load3DImage(void* target, ::Orthanc::PixelFormat format, LAAW_INT64 lineStride, LAAW_INT64 stackStride); - inline void Load3DImage(void* target, ::Orthanc::PixelFormat format, LAAW_INT64 lineStride, LAAW_INT64 stackStride, float progress[]); - }; -} - -namespace OrthancClient -{ - /** - * @brief Connection to a study stored in %Orthanc. - * - * This class encapsulates a connection to a study from a remote instance of %Orthanc. - * - **/ - class Study - { - friend class ::OrthancClient::OrthancConnection; - friend class ::OrthancClient::Patient; - friend class ::OrthancClient::Series; - friend class ::OrthancClient::Instance; - private: - bool isReference_; - Study& operator= (const Study&); // Assignment is forbidden - void* pimpl_; - Study(void* pimpl) : isReference_(true), pimpl_(pimpl) {} - public: - /** - * @brief Construct a new reference to this object. - * - * Construct a new reference to this object. Pay attention to the fact that when the referenced object is deleted, the content of this object will be invalid. - * - * @param other The original object. - **/ - Study(const Study& other) : isReference_(true), pimpl_(other.pimpl_) { } - inline Study(::OrthancClient::OrthancConnection& connection, const ::std::string& id); - inline ~Study(); - inline void Reload(); - inline LAAW_UINT32 GetSeriesCount(); - inline ::OrthancClient::Series GetSeries(LAAW_UINT32 index); - inline ::std::string GetId() const; - inline ::std::string GetMainDicomTag(const ::std::string& tag, const ::std::string& defaultValue) const; - }; -} - -namespace OrthancClient -{ - /** - * @brief Connection to an instance stored in %Orthanc. - * - * This class encapsulates a connection to an image instance from a remote instance of %Orthanc. - * - **/ - class Instance - { - friend class ::OrthancClient::OrthancConnection; - friend class ::OrthancClient::Patient; - friend class ::OrthancClient::Series; - friend class ::OrthancClient::Study; - private: - bool isReference_; - Instance& operator= (const Instance&); // Assignment is forbidden - void* pimpl_; - Instance(void* pimpl) : isReference_(true), pimpl_(pimpl) {} - public: - /** - * @brief Construct a new reference to this object. - * - * Construct a new reference to this object. Pay attention to the fact that when the referenced object is deleted, the content of this object will be invalid. - * - * @param other The original object. - **/ - Instance(const Instance& other) : isReference_(true), pimpl_(other.pimpl_) { } - inline Instance(::OrthancClient::OrthancConnection& connection, const ::std::string& id); - inline ~Instance(); - inline ::std::string GetId() const; - inline void SetImageExtractionMode(::Orthanc::ImageExtractionMode mode); - inline ::Orthanc::ImageExtractionMode GetImageExtractionMode() const; - inline ::std::string GetTagAsString(const ::std::string& tag) const; - inline float GetTagAsFloat(const ::std::string& tag) const; - inline LAAW_INT32 GetTagAsInt(const ::std::string& tag) const; - inline LAAW_UINT32 GetWidth(); - inline LAAW_UINT32 GetHeight(); - inline LAAW_UINT32 GetPitch(); - inline ::Orthanc::PixelFormat GetPixelFormat(); - inline const void* GetBuffer(); - inline const void* GetBuffer(LAAW_UINT32 y); - inline LAAW_UINT64 GetDicomSize(); - inline const void* GetDicom(); - inline void DiscardImage(); - inline void DiscardDicom(); - inline void LoadTagContent(const ::std::string& path); - inline ::std::string GetLoadedTagContent() const; - }; -} - -namespace OrthancClient -{ - /** - * @brief Create a connection to an instance of %Orthanc. - * - * Create a connection to an instance of %Orthanc. - * - * @param orthancUrl URL to which the REST API of %Orthanc is listening. - **/ - inline OrthancConnection::OrthancConnection(const ::std::string& orthancUrl) - { - isReference_ = false; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void**, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(0); - char* error = function(&pimpl_, orthancUrl.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Create a connection to an instance of %Orthanc, with authentication. - * - * Create a connection to an instance of %Orthanc, with authentication. - * - * @param orthancUrl URL to which the REST API of %Orthanc is listening. - * @param username The username. - * @param password The password. - **/ - inline OrthancConnection::OrthancConnection(const ::std::string& orthancUrl, const ::std::string& username, const ::std::string& password) - { - isReference_ = false; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void**, const char*, const char*, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(1); - char* error = function(&pimpl_, orthancUrl.c_str(), username.c_str(), password.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Destructs the object. - * - * Destructs the object. - * - **/ - inline OrthancConnection::~OrthancConnection() - { - if (isReference_) return; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(2); - char* error = function(pimpl_); - error = error; // Remove warning about unused variable - } - /** - * @brief Returns the number of threads for this connection. - * - * Returns the number of simultaneous connections that are used when downloading information from this instance of %Orthanc. - * - * @return The number of threads. - **/ - inline LAAW_UINT32 OrthancConnection::GetThreadCount() const - { - LAAW_UINT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, LAAW_UINT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(3); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Sets the number of threads for this connection. - * - * Sets the number of simultaneous connections that are used when downloading information from this instance of %Orthanc. - * - * @param threadCount The number of threads. - **/ - inline void OrthancConnection::SetThreadCount(LAAW_UINT32 threadCount) - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(4); - char* error = function(pimpl_, threadCount); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Reload the list of the patients. - * - * This method will reload the list of the patients from the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated. - * - **/ - inline void OrthancConnection::Reload() - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(5); - char* error = function(pimpl_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Returns the URL of this instance of %Orthanc. - * - * Returns the URL of the remote %Orthanc instance to which this object is connected. - * - * @return The URL. - **/ - inline ::std::string OrthancConnection::GetOrthancUrl() const - { - const char* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(6); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return std::string(result_); - } - /** - * @brief Returns the number of patients. - * - * Returns the number of patients that are stored in the remote instance of %Orthanc. - * - * @return The number of patients. - **/ - inline LAAW_UINT32 OrthancConnection::GetPatientCount() - { - LAAW_UINT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(7); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get some patient. - * - * This method will return an object that contains information about some patient. The patients are indexed by a number between 0 (inclusive) and the result of GetPatientCount() (exclusive). - * - * @param index The index of the patient of interest. - * @return The patient. - **/ - inline ::OrthancClient::Patient OrthancConnection::GetPatient(LAAW_UINT32 index) - { - void* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, void**, LAAW_UINT32); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(8); - char* error = function(pimpl_, &result_, index); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return ::OrthancClient::Patient(result_); - } - /** - * @brief Delete some patient. - * - * Delete some patient from the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated. - * - * @param index The index of the patient of interest. - * @return The patient. - **/ - inline void OrthancConnection::DeletePatient(LAAW_UINT32 index) - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(9); - char* error = function(pimpl_, index); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Send a DICOM file. - * - * This method will store a DICOM file in the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated. - * - * @param filename Path to the DICOM file - **/ - inline void OrthancConnection::StoreFile(const ::std::string& filename) - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(10); - char* error = function(pimpl_, filename.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Send a DICOM file that is contained inside a memory buffer. - * - * This method will store a DICOM file in the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated. - * - * @param dicom The memory buffer containing the DICOM file. - * @param size The size of the DICOM file. - **/ - inline void OrthancConnection::Store(const void* dicom, LAAW_UINT64 size) - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, const void*, LAAW_UINT64); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(11); - char* error = function(pimpl_, dicom, size); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } -} - -namespace OrthancClient -{ - /** - * @brief Create a connection to some patient. - * - * Create a connection to some patient. - * - * @param connection The remote instance of %Orthanc. - * @param id The %Orthanc identifier of the patient. - **/ - inline Patient::Patient(::OrthancClient::OrthancConnection& connection, const ::std::string& id) - { - isReference_ = false; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void**, void*, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(12); - char* error = function(&pimpl_, connection.pimpl_, id.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Destructs the object. - * - * Destructs the object. - * - **/ - inline Patient::~Patient() - { - if (isReference_) return; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(13); - char* error = function(pimpl_); - error = error; // Remove warning about unused variable - } - /** - * @brief Reload the studies of this patient. - * - * This method will reload the list of the studies of this patient. Pay attention to the fact that the studies that have been previously returned by GetStudy() will be invalidated. - * - **/ - inline void Patient::Reload() - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(14); - char* error = function(pimpl_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Return the number of studies for this patient. - * - * Return the number of studies for this patient. - * - * @return The number of studies. - **/ - inline LAAW_UINT32 Patient::GetStudyCount() - { - LAAW_UINT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(15); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get some study of this patient. - * - * This method will return an object that contains information about some study. The studies are indexed by a number between 0 (inclusive) and the result of GetStudyCount() (exclusive). - * - * @param index The index of the study of interest. - * @return The study. - **/ - inline ::OrthancClient::Study Patient::GetStudy(LAAW_UINT32 index) - { - void* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, void**, LAAW_UINT32); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(16); - char* error = function(pimpl_, &result_, index); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return ::OrthancClient::Study(result_); - } - /** - * @brief Get the %Orthanc identifier of this patient. - * - * Get the %Orthanc identifier of this patient. - * - * @return The identifier. - **/ - inline ::std::string Patient::GetId() const - { - const char* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(17); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return std::string(result_); - } - /** - * @brief Get the value of one of the main DICOM tags for this patient. - * - * Get the value of one of the main DICOM tags for this patient. - * - * @param tag The name of the tag of interest ("PatientName", "PatientID", "PatientSex" or "PatientBirthDate"). - * @param defaultValue The default value to be returned if this tag does not exist. - * @return The value of the tag. - **/ - inline ::std::string Patient::GetMainDicomTag(const ::std::string& tag, const ::std::string& defaultValue) const - { - const char* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**, const char*, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(18); - char* error = function(pimpl_, &result_, tag.c_str(), defaultValue.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return std::string(result_); - } -} - -namespace OrthancClient -{ - /** - * @brief Create a connection to some series. - * - * Create a connection to some series. - * - * @param connection The remote instance of %Orthanc. - * @param id The %Orthanc identifier of the series. - **/ - inline Series::Series(::OrthancClient::OrthancConnection& connection, const ::std::string& id) - { - isReference_ = false; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void**, void*, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(19); - char* error = function(&pimpl_, connection.pimpl_, id.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Destructs the object. - * - * Destructs the object. - * - **/ - inline Series::~Series() - { - if (isReference_) return; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(20); - char* error = function(pimpl_); - error = error; // Remove warning about unused variable - } - /** - * @brief Reload the instances of this series. - * - * This method will reload the list of the instances of this series. Pay attention to the fact that the instances that have been previously returned by GetInstance() will be invalidated. - * - **/ - inline void Series::Reload() - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(21); - char* error = function(pimpl_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Return the number of instances for this series. - * - * Return the number of instances for this series. - * - * @return The number of instances. - **/ - inline LAAW_UINT32 Series::GetInstanceCount() - { - LAAW_UINT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(22); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get some instance of this series. - * - * This method will return an object that contains information about some instance. The instances are indexed by a number between 0 (inclusive) and the result of GetInstanceCount() (exclusive). - * - * @param index The index of the instance of interest. - * @return The instance. - **/ - inline ::OrthancClient::Instance Series::GetInstance(LAAW_UINT32 index) - { - void* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, void**, LAAW_UINT32); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(23); - char* error = function(pimpl_, &result_, index); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return ::OrthancClient::Instance(result_); - } - /** - * @brief Get the %Orthanc identifier of this series. - * - * Get the %Orthanc identifier of this series. - * - * @return The identifier. - **/ - inline ::std::string Series::GetId() const - { - const char* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(24); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return std::string(result_); - } - /** - * @brief Returns the URL to this series. - * - * Returns the URL to this series. - * - * @return The URL. - **/ - inline ::std::string Series::GetUrl() const - { - const char* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(25); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return std::string(result_); - } - /** - * @brief Get the value of one of the main DICOM tags for this series. - * - * Get the value of one of the main DICOM tags for this series. - * - * @param tag The name of the tag of interest ("Modality", "Manufacturer", "SeriesDate", "SeriesDescription", "SeriesInstanceUID"...). - * @param defaultValue The default value to be returned if this tag does not exist. - * @return The value of the tag. - **/ - inline ::std::string Series::GetMainDicomTag(const ::std::string& tag, const ::std::string& defaultValue) const - { - const char* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**, const char*, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(26); - char* error = function(pimpl_, &result_, tag.c_str(), defaultValue.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return std::string(result_); - } - /** - * @brief Test whether this series encodes a 3D image that can be downloaded from %Orthanc. - * - * Test whether this series encodes a 3D image that can be downloaded from %Orthanc. - * - * @return "true" if and only if this is a 3D image. - **/ - inline bool Series::Is3DImage() - { - LAAW_INT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_INT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(27); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_ != 0; - } - /** - * @brief Get the width of the 3D image. - * - * Get the width of the 3D image (i.e. along the X-axis). This call is only valid if this series corresponds to a 3D image. - * - * @return The width. - **/ - inline LAAW_UINT32 Series::GetWidth() - { - LAAW_UINT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(28); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get the height of the 3D image. - * - * Get the height of the 3D image (i.e. along the Y-axis). This call is only valid if this series corresponds to a 3D image. - * - * @return The height. - **/ - inline LAAW_UINT32 Series::GetHeight() - { - LAAW_UINT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(29); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get the physical size of a voxel along the X-axis. - * - * Get the physical size of a voxel along the X-axis. This call is only valid if this series corresponds to a 3D image. - * - * @return The voxel size. - **/ - inline float Series::GetVoxelSizeX() - { - float result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, float*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(30); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get the physical size of a voxel along the Y-axis. - * - * Get the physical size of a voxel along the Y-axis. This call is only valid if this series corresponds to a 3D image. - * - * @return The voxel size. - **/ - inline float Series::GetVoxelSizeY() - { - float result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, float*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(31); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get the physical size of a voxel along the Z-axis. - * - * Get the physical size of a voxel along the Z-axis. This call is only valid if this series corresponds to a 3D image. - * - * @return The voxel size. - **/ - inline float Series::GetVoxelSizeZ() - { - float result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, float*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(32); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get the slice thickness. - * - * Get the slice thickness. This call is only valid if this series corresponds to a 3D image. - * - * @return The slice thickness. - **/ - inline float Series::GetSliceThickness() - { - float result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, float*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(33); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Load the 3D image into a memory buffer. - * - * Load the 3D image into a memory buffer. This call is only valid if this series corresponds to a 3D image. The "target" buffer must be wide enough to store all the voxels of the image. - * - * @param target The target memory buffer. - * @param format The memory layout of the voxels. - * @param lineStride The number of bytes between two lines in the target memory buffer. - * @param stackStride The number of bytes between two 2D slices in the target memory buffer. - **/ - inline void Series::Load3DImage(void* target, ::Orthanc::PixelFormat format, LAAW_INT64 lineStride, LAAW_INT64 stackStride) - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, void*, LAAW_INT32, LAAW_INT64, LAAW_INT64); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(34); - char* error = function(pimpl_, target, format, lineStride, stackStride); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Load the 3D image into a memory buffer. - * - * Load the 3D image into a memory buffer. This call is only valid if this series corresponds to a 3D image. The "target" buffer must be wide enough to store all the voxels of the image. This method will also update a progress indicator to monitor the loading of the image. - * - * @param target The target memory buffer. - * @param format The memory layout of the voxels. - * @param lineStride The number of bytes between two lines in the target memory buffer. - * @param stackStride The number of bytes between two 2D slices in the target memory buffer. - * @param progress A pointer to a floating-point number that is continuously updated by the download threads to reflect the percentage of completion (between 0 and 1). This value can be read from a separate thread. - **/ - inline void Series::Load3DImage(void* target, ::Orthanc::PixelFormat format, LAAW_INT64 lineStride, LAAW_INT64 stackStride, float progress[]) - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, void*, LAAW_INT32, LAAW_INT64, LAAW_INT64, float*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(35); - char* error = function(pimpl_, target, format, lineStride, stackStride, progress); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } -} - -namespace OrthancClient -{ - /** - * @brief Create a connection to some study. - * - * Create a connection to some study. - * - * @param connection The remote instance of %Orthanc. - * @param id The %Orthanc identifier of the study. - **/ - inline Study::Study(::OrthancClient::OrthancConnection& connection, const ::std::string& id) - { - isReference_ = false; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void**, void*, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(36); - char* error = function(&pimpl_, connection.pimpl_, id.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Destructs the object. - * - * Destructs the object. - * - **/ - inline Study::~Study() - { - if (isReference_) return; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(37); - char* error = function(pimpl_); - error = error; // Remove warning about unused variable - } - /** - * @brief Reload the series of this study. - * - * This method will reload the list of the series of this study. Pay attention to the fact that the series that have been previously returned by GetSeries() will be invalidated. - * - **/ - inline void Study::Reload() - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(38); - char* error = function(pimpl_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Return the number of series for this study. - * - * Return the number of series for this study. - * - * @return The number of series. - **/ - inline LAAW_UINT32 Study::GetSeriesCount() - { - LAAW_UINT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(39); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get some series of this study. - * - * This method will return an object that contains information about some series. The series are indexed by a number between 0 (inclusive) and the result of GetSeriesCount() (exclusive). - * - * @param index The index of the series of interest. - * @return The series. - **/ - inline ::OrthancClient::Series Study::GetSeries(LAAW_UINT32 index) - { - void* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, void**, LAAW_UINT32); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(40); - char* error = function(pimpl_, &result_, index); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return ::OrthancClient::Series(result_); - } - /** - * @brief Get the %Orthanc identifier of this study. - * - * Get the %Orthanc identifier of this study. - * - * @return The identifier. - **/ - inline ::std::string Study::GetId() const - { - const char* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(41); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return std::string(result_); - } - /** - * @brief Get the value of one of the main DICOM tags for this study. - * - * Get the value of one of the main DICOM tags for this study. - * - * @param tag The name of the tag of interest ("StudyDate", "StudyDescription", "StudyInstanceUID" or "StudyTime"). - * @param defaultValue The default value to be returned if this tag does not exist. - * @return The value of the tag. - **/ - inline ::std::string Study::GetMainDicomTag(const ::std::string& tag, const ::std::string& defaultValue) const - { - const char* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**, const char*, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(42); - char* error = function(pimpl_, &result_, tag.c_str(), defaultValue.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return std::string(result_); - } -} - -namespace OrthancClient -{ - /** - * @brief Create a connection to some image instance. - * - * Create a connection to some image instance. - * - * @param connection The remote instance of %Orthanc. - * @param id The %Orthanc identifier of the image instance. - **/ - inline Instance::Instance(::OrthancClient::OrthancConnection& connection, const ::std::string& id) - { - isReference_ = false; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void**, void*, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(43); - char* error = function(&pimpl_, connection.pimpl_, id.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Destructs the object. - * - * Destructs the object. - * - **/ - inline Instance::~Instance() - { - if (isReference_) return; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(44); - char* error = function(pimpl_); - error = error; // Remove warning about unused variable - } - /** - * @brief Get the %Orthanc identifier of this identifier. - * - * Get the %Orthanc identifier of this identifier. - * - * @return The identifier. - **/ - inline ::std::string Instance::GetId() const - { - const char* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(45); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return std::string(result_); - } - /** - * @brief Set the extraction mode for the 2D image corresponding to this instance. - * - * Set the extraction mode for the 2D image corresponding to this instance. - * - * @param mode The extraction mode. - **/ - inline void Instance::SetImageExtractionMode(::Orthanc::ImageExtractionMode mode) - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_INT32); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(46); - char* error = function(pimpl_, mode); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Get the extraction mode for the 2D image corresponding to this instance. - * - * Get the extraction mode for the 2D image corresponding to this instance. - * - * @return The extraction mode. - **/ - inline ::Orthanc::ImageExtractionMode Instance::GetImageExtractionMode() const - { - LAAW_INT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, LAAW_INT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(47); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return static_cast< ::Orthanc::ImageExtractionMode >(result_); - } - /** - * @brief Get the string value of some DICOM tag of this instance. - * - * Get the string value of some DICOM tag of this instance. - * - * @param tag The name of the tag of interest. - * @return The value of the tag. - **/ - inline ::std::string Instance::GetTagAsString(const ::std::string& tag) const - { - const char* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(48); - char* error = function(pimpl_, &result_, tag.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return std::string(result_); - } - /** - * @brief Get the floating point value that is stored in some DICOM tag of this instance. - * - * Get the floating point value that is stored in some DICOM tag of this instance. - * - * @param tag The name of the tag of interest. - * @return The value of the tag. - **/ - inline float Instance::GetTagAsFloat(const ::std::string& tag) const - { - float result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, float*, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(49); - char* error = function(pimpl_, &result_, tag.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get the integer value that is stored in some DICOM tag of this instance. - * - * Get the integer value that is stored in some DICOM tag of this instance. - * - * @param tag The name of the tag of interest. - * @return The value of the tag. - **/ - inline LAAW_INT32 Instance::GetTagAsInt(const ::std::string& tag) const - { - LAAW_INT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, LAAW_INT32*, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(50); - char* error = function(pimpl_, &result_, tag.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get the width of the 2D image. - * - * Get the width of the 2D image that is encoded by this DICOM instance. - * - * @return The width. - **/ - inline LAAW_UINT32 Instance::GetWidth() - { - LAAW_UINT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(51); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get the height of the 2D image. - * - * Get the height of the 2D image that is encoded by this DICOM instance. - * - * @return The height. - **/ - inline LAAW_UINT32 Instance::GetHeight() - { - LAAW_UINT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(52); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get the number of bytes between two lines of the image (pitch). - * - * Get the number of bytes between two lines of the image in the memory buffer returned by GetBuffer(). This value depends on the extraction mode for the image. - * - * @return The pitch. - **/ - inline LAAW_UINT32 Instance::GetPitch() - { - LAAW_UINT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(53); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get the format of the pixels of the 2D image. - * - * Return the memory layout that is used for the 2D image that is encoded by this DICOM instance. This value depends on the extraction mode for the image. - * - * @return The pixel format. - **/ - inline ::Orthanc::PixelFormat Instance::GetPixelFormat() - { - LAAW_INT32 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_INT32*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(54); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return static_cast< ::Orthanc::PixelFormat >(result_); - } - /** - * @brief Access the memory buffer in which the raw pixels of the 2D image are stored. - * - * Access the memory buffer in which the raw pixels of the 2D image are stored. - * - * @return A pointer to the memory buffer. - **/ - inline const void* Instance::GetBuffer() - { - const void* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, const void**); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(55); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return reinterpret_cast< const void* >(result_); - } - /** - * @brief Access the memory buffer in which the raw pixels of some line of the 2D image are stored. - * - * Access the memory buffer in which the raw pixels of some line of the 2D image are stored. - * - * @param y The line of interest. - * @return A pointer to the memory buffer. - **/ - inline const void* Instance::GetBuffer(LAAW_UINT32 y) - { - const void* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, const void**, LAAW_UINT32); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(56); - char* error = function(pimpl_, &result_, y); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return reinterpret_cast< const void* >(result_); - } - /** - * @brief Get the size of the DICOM file corresponding to this instance. - * - * Get the size of the DICOM file corresponding to this instance. - * - * @return The file size. - **/ - inline LAAW_UINT64 Instance::GetDicomSize() - { - LAAW_UINT64 result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT64*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(57); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return result_; - } - /** - * @brief Get a pointer to the content of the DICOM file corresponding to this instance. - * - * Get a pointer to the content of the DICOM file corresponding to this instance. - * - * @return The DICOM file. - **/ - inline const void* Instance::GetDicom() - { - const void* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, const void**); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(58); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return reinterpret_cast< const void* >(result_); - } - /** - * @brief Discard the downloaded 2D image, so as to make room in memory. - * - * Discard the downloaded 2D image, so as to make room in memory. - * - **/ - inline void Instance::DiscardImage() - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(59); - char* error = function(pimpl_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Discard the downloaded DICOM file, so as to make room in memory. - * - * Discard the downloaded DICOM file, so as to make room in memory. - * - **/ - inline void Instance::DiscardDicom() - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(60); - char* error = function(pimpl_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Load a raw tag from the DICOM file. - * - * Load a raw tag from the DICOM file. - * - * @param path The path to the tag of interest (e.g. "0020-000d"). - **/ - inline void Instance::LoadTagContent(const ::std::string& path) - { - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, const char*); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(61); - char* error = function(pimpl_, path.c_str()); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - } - /** - * @brief Return the value of the raw tag that was loaded by LoadContent. - * - * Return the value of the raw tag that was loaded by LoadContent. - * - * @return The tag value. - **/ - inline ::std::string Instance::GetLoadedTagContent() const - { - const char* result_; - typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**); - Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(62); - char* error = function(pimpl_, &result_); - ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error); - return std::string(result_); - } -} - diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.def --- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.def Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -LIBRARY some.dll -EXPORTS - _LAAW_EXTERNC_557aee7b61817292a0f31269d3c35db7@8 = LAAW_EXTERNC_557aee7b61817292a0f31269d3c35db7@8 - _LAAW_EXTERNC_0b8dff0ce67f10954a49b059e348837e@8 = LAAW_EXTERNC_0b8dff0ce67f10954a49b059e348837e@8 - _LAAW_EXTERNC_e05097c153f676e5a5ee54dcfc78256f@4 = LAAW_EXTERNC_e05097c153f676e5a5ee54dcfc78256f@4 - _LAAW_EXTERNC_e840242bf58d17d3c1d722da09ce88e0@8 = LAAW_EXTERNC_e840242bf58d17d3c1d722da09ce88e0@8 - _LAAW_EXTERNC_c9af31433001b5dfc012a552dc6d0050@8 = LAAW_EXTERNC_c9af31433001b5dfc012a552dc6d0050@8 - _LAAW_EXTERNC_3fba4d6b818180a44cd1cae6046334dc@12 = LAAW_EXTERNC_3fba4d6b818180a44cd1cae6046334dc@12 - _LAAW_EXTERNC_aeb20dc75b9246188db857317e5e0ce7@8 = LAAW_EXTERNC_aeb20dc75b9246188db857317e5e0ce7@8 - _LAAW_EXTERNC_62689803d9871e4d9c51a648640b320b@8 = LAAW_EXTERNC_62689803d9871e4d9c51a648640b320b@8 - _LAAW_EXTERNC_2fb64c9e5a67eccd413b0e913469a421@16 = LAAW_EXTERNC_2fb64c9e5a67eccd413b0e913469a421@16 - _LAAW_EXTERNC_1f1acb322ea4d0aad65172824607673c@8 = LAAW_EXTERNC_1f1acb322ea4d0aad65172824607673c@8 - _LAAW_EXTERNC_f3fd272e4636f6a531aabb72ee01cd5b@16 = LAAW_EXTERNC_f3fd272e4636f6a531aabb72ee01cd5b@16 - _LAAW_EXTERNC_12d3de0a96e9efb11136a9811bb9ed38@4 = LAAW_EXTERNC_12d3de0a96e9efb11136a9811bb9ed38@4 - _LAAW_EXTERNC_f756172daf04516eec3a566adabb4335@4 = LAAW_EXTERNC_f756172daf04516eec3a566adabb4335@4 - _LAAW_EXTERNC_ddb68763ec902a97d579666a73a20118@8 = LAAW_EXTERNC_ddb68763ec902a97d579666a73a20118@8 - _LAAW_EXTERNC_fba3c68b4be7558dbc65f7ce1ab57d63@12 = LAAW_EXTERNC_fba3c68b4be7558dbc65f7ce1ab57d63@12 - _LAAW_EXTERNC_b4ca99d958f843493e58d1ef967340e1@8 = LAAW_EXTERNC_b4ca99d958f843493e58d1ef967340e1@8 - _LAAW_EXTERNC_78d5cc76d282437b6f93ec3b82c35701@16 = LAAW_EXTERNC_78d5cc76d282437b6f93ec3b82c35701@16 - _LAAW_EXTERNC_6cf0d7268667f9b0aa4511bacf184919@12 = LAAW_EXTERNC_6cf0d7268667f9b0aa4511bacf184919@12 - _LAAW_EXTERNC_7d81cd502ee27e859735d0ea7112b5a1@4 = LAAW_EXTERNC_7d81cd502ee27e859735d0ea7112b5a1@4 - _LAAW_EXTERNC_48a2a1a9d68c047e22bfba23014643d2@4 = LAAW_EXTERNC_48a2a1a9d68c047e22bfba23014643d2@4 - _LAAW_EXTERNC_852bf8296ca21c5fde5ec565cc10721d@8 = LAAW_EXTERNC_852bf8296ca21c5fde5ec565cc10721d@8 - _LAAW_EXTERNC_efd04574e0779faa83df1f2d8f9888db@12 = LAAW_EXTERNC_efd04574e0779faa83df1f2d8f9888db@12 - _LAAW_EXTERNC_736247ff5e8036dac38163da6f666ed5@8 = LAAW_EXTERNC_736247ff5e8036dac38163da6f666ed5@8 - _LAAW_EXTERNC_d82d2598a7a73f4b6fcc0c09c25b08ca@8 = LAAW_EXTERNC_d82d2598a7a73f4b6fcc0c09c25b08ca@8 - _LAAW_EXTERNC_88134b978f9acb2aecdadf54aeab3c64@16 = LAAW_EXTERNC_88134b978f9acb2aecdadf54aeab3c64@16 - _LAAW_EXTERNC_152cb1b704c053d24b0dab7461ba6ea3@8 = LAAW_EXTERNC_152cb1b704c053d24b0dab7461ba6ea3@8 - _LAAW_EXTERNC_eee03f337ec81d9f1783cd41e5238757@8 = LAAW_EXTERNC_eee03f337ec81d9f1783cd41e5238757@8 - _LAAW_EXTERNC_006f08237bd7611636fc721baebfb4c5@8 = LAAW_EXTERNC_006f08237bd7611636fc721baebfb4c5@8 - _LAAW_EXTERNC_b794f5cd3dad7d7b575dd1fd902afdd0@8 = LAAW_EXTERNC_b794f5cd3dad7d7b575dd1fd902afdd0@8 - _LAAW_EXTERNC_8ee2e50dd9df8f66a3c1766090dd03ab@8 = LAAW_EXTERNC_8ee2e50dd9df8f66a3c1766090dd03ab@8 - _LAAW_EXTERNC_046aed35bbe4751691f4c34cc249a61d@8 = LAAW_EXTERNC_046aed35bbe4751691f4c34cc249a61d@8 - _LAAW_EXTERNC_2be452e7af5bf7dfd8c5021842674497@8 = LAAW_EXTERNC_2be452e7af5bf7dfd8c5021842674497@8 - _LAAW_EXTERNC_4dcc7a0fd025efba251ac6e9b701c2c5@28 = LAAW_EXTERNC_4dcc7a0fd025efba251ac6e9b701c2c5@28 - _LAAW_EXTERNC_b2601a161c24ad0a1d3586246f87452c@32 = LAAW_EXTERNC_b2601a161c24ad0a1d3586246f87452c@32 - _LAAW_EXTERNC_193599b9e345384fcdfcd47c29c55342@12 = LAAW_EXTERNC_193599b9e345384fcdfcd47c29c55342@12 - _LAAW_EXTERNC_7c97f17063a357d38c5fab1136ad12a0@4 = LAAW_EXTERNC_7c97f17063a357d38c5fab1136ad12a0@4 - _LAAW_EXTERNC_e65b20b7e0170b67544cd6664a4639b7@4 = LAAW_EXTERNC_e65b20b7e0170b67544cd6664a4639b7@4 - _LAAW_EXTERNC_470e981b0e41f17231ba0ae6f3033321@8 = LAAW_EXTERNC_470e981b0e41f17231ba0ae6f3033321@8 - _LAAW_EXTERNC_04cefd138b6ea15ad909858f2a0a8f05@12 = LAAW_EXTERNC_04cefd138b6ea15ad909858f2a0a8f05@12 - _LAAW_EXTERNC_aee5b1f6f0c082f2c3b0986f9f6a18c7@8 = LAAW_EXTERNC_aee5b1f6f0c082f2c3b0986f9f6a18c7@8 - _LAAW_EXTERNC_93965682bace75491413e1f0b8d5a654@16 = LAAW_EXTERNC_93965682bace75491413e1f0b8d5a654@16 - _LAAW_EXTERNC_b01c6003238eb46c8db5dc823d7ca678@12 = LAAW_EXTERNC_b01c6003238eb46c8db5dc823d7ca678@12 - _LAAW_EXTERNC_0147007fb99bad8cd95a139ec8795376@4 = LAAW_EXTERNC_0147007fb99bad8cd95a139ec8795376@4 - _LAAW_EXTERNC_236ee8b403bc99535a8a4695c0cd45cb@8 = LAAW_EXTERNC_236ee8b403bc99535a8a4695c0cd45cb@8 - _LAAW_EXTERNC_2a437b7aba6bb01e81113835be8f0146@8 = LAAW_EXTERNC_2a437b7aba6bb01e81113835be8f0146@8 - _LAAW_EXTERNC_2bcbcb850934ae0bb4c6f0cc940e6cda@8 = LAAW_EXTERNC_2bcbcb850934ae0bb4c6f0cc940e6cda@8 - _LAAW_EXTERNC_8d415c3a78a48e7e61d9fd24e7c79484@12 = LAAW_EXTERNC_8d415c3a78a48e7e61d9fd24e7c79484@12 - _LAAW_EXTERNC_70d2f8398bbc63b5f792b69b4ad5fecb@12 = LAAW_EXTERNC_70d2f8398bbc63b5f792b69b4ad5fecb@12 - _LAAW_EXTERNC_1729a067d902771517388eedd7346b23@12 = LAAW_EXTERNC_1729a067d902771517388eedd7346b23@12 - _LAAW_EXTERNC_72e2aeee66cd3abd8ab7e987321c3745@8 = LAAW_EXTERNC_72e2aeee66cd3abd8ab7e987321c3745@8 - _LAAW_EXTERNC_1ea3df5a1ac1a1a687fe7325adddb6f0@8 = LAAW_EXTERNC_1ea3df5a1ac1a1a687fe7325adddb6f0@8 - _LAAW_EXTERNC_99b4f370e4f532d8b763e2cb49db92f8@8 = LAAW_EXTERNC_99b4f370e4f532d8b763e2cb49db92f8@8 - _LAAW_EXTERNC_c41c742b68617f1c0590577a0a5ebc0c@8 = LAAW_EXTERNC_c41c742b68617f1c0590577a0a5ebc0c@8 - _LAAW_EXTERNC_142dd2feba0fc1d262bbd0baeb441a8b@8 = LAAW_EXTERNC_142dd2feba0fc1d262bbd0baeb441a8b@8 - _LAAW_EXTERNC_5f5c9f81a4dff8daa6c359f1d0488fef@12 = LAAW_EXTERNC_5f5c9f81a4dff8daa6c359f1d0488fef@12 - _LAAW_EXTERNC_9ca979fffd08fa256306d4e68d8b0e91@8 = LAAW_EXTERNC_9ca979fffd08fa256306d4e68d8b0e91@8 - _LAAW_EXTERNC_6f2d77a26edc91c28d89408dbc3c271e@8 = LAAW_EXTERNC_6f2d77a26edc91c28d89408dbc3c271e@8 - _LAAW_EXTERNC_c0f494b80d4ff8b232df7a75baa0700a@4 = LAAW_EXTERNC_c0f494b80d4ff8b232df7a75baa0700a@4 - _LAAW_EXTERNC_d604f44bd5195e082e745e9cbc164f4c@4 = LAAW_EXTERNC_d604f44bd5195e082e745e9cbc164f4c@4 - _LAAW_EXTERNC_1710299d1c5f3b1f2b7cf3962deebbfd@8 = LAAW_EXTERNC_1710299d1c5f3b1f2b7cf3962deebbfd@8 - _LAAW_EXTERNC_bb55aaf772ddceaadee36f4e54136bcb@8 = LAAW_EXTERNC_bb55aaf772ddceaadee36f4e54136bcb@8 - _LAAW_EXTERNC_6c5ad02f91b583e29cebd0bd319ce21d@12 = LAAW_EXTERNC_6c5ad02f91b583e29cebd0bd319ce21d@12 - _LAAW_EXTERNC_4068241c44a9c1367fe0e57be523f207@4 = LAAW_EXTERNC_4068241c44a9c1367fe0e57be523f207@4 - _LAAW_EXTERNC_GetDescription@0 = LAAW_EXTERNC_GetDescription@0 - _LAAW_EXTERNC_GetCompany@0 = LAAW_EXTERNC_GetCompany@0 - _LAAW_EXTERNC_GetProduct@0 = LAAW_EXTERNC_GetProduct@0 - _LAAW_EXTERNC_GetCopyright@0 = LAAW_EXTERNC_GetCopyright@0 - _LAAW_EXTERNC_GetVersion@0 = LAAW_EXTERNC_GetVersion@0 - _LAAW_EXTERNC_GetFileVersion@0 = LAAW_EXTERNC_GetFileVersion@0 - _LAAW_EXTERNC_GetFullVersion@0 = LAAW_EXTERNC_GetFullVersion@0 - _LAAW_EXTERNC_FreeString@4 = LAAW_EXTERNC_FreeString@4 diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.rc --- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.rc Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -#include - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,8,0,5 - PRODUCTVERSION 0,8,0,0 - FILEOS VOS_NT_WINDOWS32 - FILETYPE VFT_DLL - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "Comments", "Release 0.8.5" - VALUE "CompanyName", "University Hospital of Liege" - VALUE "FileDescription", "Native client to the REST API of Orthanc" - VALUE "FileVersion", "0.8.0.5" - VALUE "InternalName", "OrthancClient" - VALUE "LegalCopyright", "(c) 2012-2015, Sebastien Jodogne, University Hospital of Liege" - VALUE "LegalTrademarks", "Licensing information is available on http://www.orthanc-server.com/" - VALUE "OriginalFilename", "OrthancClient_Windows32.dll" - VALUE "ProductName", "OrthancClient" - VALUE "ProductVersion", "0.8" - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 // U.S. English - END - END diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.def --- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.def Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -LIBRARY some.dll -EXPORTS - LAAW_EXTERNC_557aee7b61817292a0f31269d3c35db7 - LAAW_EXTERNC_0b8dff0ce67f10954a49b059e348837e - LAAW_EXTERNC_e05097c153f676e5a5ee54dcfc78256f - LAAW_EXTERNC_e840242bf58d17d3c1d722da09ce88e0 - LAAW_EXTERNC_c9af31433001b5dfc012a552dc6d0050 - LAAW_EXTERNC_3fba4d6b818180a44cd1cae6046334dc - LAAW_EXTERNC_aeb20dc75b9246188db857317e5e0ce7 - LAAW_EXTERNC_62689803d9871e4d9c51a648640b320b - LAAW_EXTERNC_2fb64c9e5a67eccd413b0e913469a421 - LAAW_EXTERNC_1f1acb322ea4d0aad65172824607673c - LAAW_EXTERNC_f3fd272e4636f6a531aabb72ee01cd5b - LAAW_EXTERNC_12d3de0a96e9efb11136a9811bb9ed38 - LAAW_EXTERNC_f756172daf04516eec3a566adabb4335 - LAAW_EXTERNC_ddb68763ec902a97d579666a73a20118 - LAAW_EXTERNC_fba3c68b4be7558dbc65f7ce1ab57d63 - LAAW_EXTERNC_b4ca99d958f843493e58d1ef967340e1 - LAAW_EXTERNC_78d5cc76d282437b6f93ec3b82c35701 - LAAW_EXTERNC_6cf0d7268667f9b0aa4511bacf184919 - LAAW_EXTERNC_7d81cd502ee27e859735d0ea7112b5a1 - LAAW_EXTERNC_48a2a1a9d68c047e22bfba23014643d2 - LAAW_EXTERNC_852bf8296ca21c5fde5ec565cc10721d - LAAW_EXTERNC_efd04574e0779faa83df1f2d8f9888db - LAAW_EXTERNC_736247ff5e8036dac38163da6f666ed5 - LAAW_EXTERNC_d82d2598a7a73f4b6fcc0c09c25b08ca - LAAW_EXTERNC_88134b978f9acb2aecdadf54aeab3c64 - LAAW_EXTERNC_152cb1b704c053d24b0dab7461ba6ea3 - LAAW_EXTERNC_eee03f337ec81d9f1783cd41e5238757 - LAAW_EXTERNC_006f08237bd7611636fc721baebfb4c5 - LAAW_EXTERNC_b794f5cd3dad7d7b575dd1fd902afdd0 - LAAW_EXTERNC_8ee2e50dd9df8f66a3c1766090dd03ab - LAAW_EXTERNC_046aed35bbe4751691f4c34cc249a61d - LAAW_EXTERNC_2be452e7af5bf7dfd8c5021842674497 - LAAW_EXTERNC_4dcc7a0fd025efba251ac6e9b701c2c5 - LAAW_EXTERNC_b2601a161c24ad0a1d3586246f87452c - LAAW_EXTERNC_193599b9e345384fcdfcd47c29c55342 - LAAW_EXTERNC_7c97f17063a357d38c5fab1136ad12a0 - LAAW_EXTERNC_e65b20b7e0170b67544cd6664a4639b7 - LAAW_EXTERNC_470e981b0e41f17231ba0ae6f3033321 - LAAW_EXTERNC_04cefd138b6ea15ad909858f2a0a8f05 - LAAW_EXTERNC_aee5b1f6f0c082f2c3b0986f9f6a18c7 - LAAW_EXTERNC_93965682bace75491413e1f0b8d5a654 - LAAW_EXTERNC_b01c6003238eb46c8db5dc823d7ca678 - LAAW_EXTERNC_0147007fb99bad8cd95a139ec8795376 - LAAW_EXTERNC_236ee8b403bc99535a8a4695c0cd45cb - LAAW_EXTERNC_2a437b7aba6bb01e81113835be8f0146 - LAAW_EXTERNC_2bcbcb850934ae0bb4c6f0cc940e6cda - LAAW_EXTERNC_8d415c3a78a48e7e61d9fd24e7c79484 - LAAW_EXTERNC_70d2f8398bbc63b5f792b69b4ad5fecb - LAAW_EXTERNC_1729a067d902771517388eedd7346b23 - LAAW_EXTERNC_72e2aeee66cd3abd8ab7e987321c3745 - LAAW_EXTERNC_1ea3df5a1ac1a1a687fe7325adddb6f0 - LAAW_EXTERNC_99b4f370e4f532d8b763e2cb49db92f8 - LAAW_EXTERNC_c41c742b68617f1c0590577a0a5ebc0c - LAAW_EXTERNC_142dd2feba0fc1d262bbd0baeb441a8b - LAAW_EXTERNC_5f5c9f81a4dff8daa6c359f1d0488fef - LAAW_EXTERNC_9ca979fffd08fa256306d4e68d8b0e91 - LAAW_EXTERNC_6f2d77a26edc91c28d89408dbc3c271e - LAAW_EXTERNC_c0f494b80d4ff8b232df7a75baa0700a - LAAW_EXTERNC_d604f44bd5195e082e745e9cbc164f4c - LAAW_EXTERNC_1710299d1c5f3b1f2b7cf3962deebbfd - LAAW_EXTERNC_bb55aaf772ddceaadee36f4e54136bcb - LAAW_EXTERNC_6c5ad02f91b583e29cebd0bd319ce21d - LAAW_EXTERNC_4068241c44a9c1367fe0e57be523f207 - LAAW_EXTERNC_GetDescription - LAAW_EXTERNC_GetCompany - LAAW_EXTERNC_GetProduct - LAAW_EXTERNC_GetCopyright - LAAW_EXTERNC_GetVersion - LAAW_EXTERNC_GetFileVersion - LAAW_EXTERNC_GetFullVersion - LAAW_EXTERNC_FreeString diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.rc --- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.rc Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -#include - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,8,0,5 - PRODUCTVERSION 0,8,0,0 - FILEOS VOS_NT_WINDOWS32 - FILETYPE VFT_DLL - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "Comments", "Release 0.8.5" - VALUE "CompanyName", "University Hospital of Liege" - VALUE "FileDescription", "Native client to the REST API of Orthanc" - VALUE "FileVersion", "0.8.0.5" - VALUE "InternalName", "OrthancClient" - VALUE "LegalCopyright", "(c) 2012-2015, Sebastien Jodogne, University Hospital of Liege" - VALUE "LegalTrademarks", "Licensing information is available on http://www.orthanc-server.com/" - VALUE "OriginalFilename", "OrthancClient_Windows64.dll" - VALUE "ProductName", "OrthancClient" - VALUE "ProductVersion", "0.8" - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 // U.S. English - END - END diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/ConfigurationCpp.json --- a/OrthancCppClient/SharedLibrary/ConfigurationCpp.json Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -{ - "InternalsNamespace" : [ "OrthancClient", "Internals" ], - "PublicNamespace" : [ "OrthancClient" ], - "ExceptionClassName" : "OrthancClientException" -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/Generate.sh --- a/OrthancCppClient/SharedLibrary/Generate.sh Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -#!/bin/bash - -set -e - -mkdir -p AUTOGENERATED -LAAW_ROOT=~/Subversion/Jomago/Src/Labo/Laaw - -${LAAW_ROOT}/Parser/Build/LaawParser.exe AUTOGENERATED/CodeModelRaw.json ../OrthancConnection.h -I`pwd`/../../s/jsoncpp-src-0.6.0-rc2/include -fms-extensions -python ${LAAW_ROOT}/Generators/CodeModelPostProcessing.py AUTOGENERATED/CodeModel.json AUTOGENERATED/CodeModelRaw.json Product.json -python ${LAAW_ROOT}/Generators/GenerateWrapperCpp.py AUTOGENERATED/OrthancCppClient.h AUTOGENERATED/CodeModel.json Product.json ConfigurationCpp.json -python ${LAAW_ROOT}/Generators/GenerateExternC.py AUTOGENERATED/ExternC.cpp AUTOGENERATED/CodeModel.json Product.json -python ${LAAW_ROOT}/Generators/GenerateWindows32Def.py AUTOGENERATED/Windows32.def AUTOGENERATED/CodeModel.json -python ${LAAW_ROOT}/Generators/GenerateWindows64Def.py AUTOGENERATED/Windows64.def AUTOGENERATED/CodeModel.json -python ${LAAW_ROOT}/Generators/ApplyProductSubstitutions.py AUTOGENERATED/Windows32.rc ${LAAW_ROOT}/Resources/DllResources.rc.mustache Product.json PLATFORM_SUFFIX "_Windows32" -python ${LAAW_ROOT}/Generators/ApplyProductSubstitutions.py AUTOGENERATED/Windows64.rc ${LAAW_ROOT}/Resources/DllResources.rc.mustache Product.json PLATFORM_SUFFIX "_Windows64" diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/Laaw/VersionScript.map --- a/OrthancCppClient/SharedLibrary/Laaw/VersionScript.map Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -# This is a version-script - -{ -global: - LAAW_EXTERNC_*; - -local: - *; -}; diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/Laaw/laaw/laaw-exports.h --- a/OrthancCppClient/SharedLibrary/Laaw/laaw/laaw-exports.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/** - * Laaw - Lightweight, Automated API Wrapper - * Copyright (C) 2010-2013 Jomago - Alain Mazy, Benjamin Golinvaux, - * Sebastien Jodogne - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -/******************************************************************** - ** Windows target - ********************************************************************/ - -#if defined _WIN32 - -#include - -#if defined(__GNUC__) -// This is Mingw -#define LAAW_EXPORT_DLL_API // The exports are handled by the .DEF file -#else -// This is MSVC -#define LAAW_EXPORT_DLL_API __declspec(dllexport) -#endif - -#ifdef _M_X64 -// 64 bits target -#define LAAW_CALL_CONVENTION -#else -// 32 bits target -#define LAAW_CALL_CONVENTION __stdcall // Use the StdCall in Windows32 (for VB6) -#endif - - -/******************************************************************** - ** Linux target - ********************************************************************/ - -#elif defined(__linux) - -// Try the gcc visibility support -// http://gcc.gnu.org/wiki/Visibility -#if ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) -#define LAAW_EXPORT_DLL_API __attribute__ ((visibility("default"))) -#define LAAW_CALL_CONVENTION -#else -#error No support for visibility in your version of GCC -#endif - - -/******************************************************************** - ** Max OS X target - ********************************************************************/ - -#else - -#define LAAW_EXPORT_DLL_API __attribute__ ((visibility("default"))) -#define LAAW_CALL_CONVENTION - -#endif diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/Laaw/laaw/laaw.h --- a/OrthancCppClient/SharedLibrary/Laaw/laaw/laaw.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,89 +0,0 @@ -/** - * Laaw - Lightweight, Automated API Wrapper - * Copyright (C) 2010-2013 Jomago - Alain Mazy, Benjamin Golinvaux, - * Sebastien Jodogne - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "laaw-exports.h" -#include -#include - -#if (LAAW_PARSING == 1) - -#define LAAW_API __attribute__((deprecated(""))) -#define LAAW_API_INTERNAL __attribute__((deprecated(""))) -#define LAAW_API_OVERLOAD(name) __attribute__((deprecated(""))) -#define LAAW_API_PROPERTY __attribute__((deprecated(""))) -#define LAAW_API_STATIC_CLASS __attribute__((deprecated(""))) -#define LAAW_API_CUSTOM(name, value) __attribute__((deprecated(""))) - -#else - -#define LAAW_API -#define LAAW_API_INTERNAL -#define LAAW_API_OVERLOAD(name) -#define LAAW_API_PROPERTY -#define LAAW_API_STATIC_CLASS -#define LAAW_API_CUSTOM(name, value) - -#endif - - -namespace Laaw -{ - /** - * This is the base class from which all the public exceptions in - * the SDK should derive. - **/ - class LaawException - { - private: - std::string what_; - - public: - LaawException() - { - } - - LaawException(const std::string& what) : what_(what) - { - } - - LaawException(const char* what) : what_(what) - { - } - - virtual const char* What() const - { - return what_.c_str(); - } - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/Product.json --- a/OrthancCppClient/SharedLibrary/Product.json Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -{ - "Product" : "OrthancClient", - "Description" : "Native client to the REST API of Orthanc", - "Company" : "University Hospital of Liege", - "Copyright" : "(c) 2012-2015, Sebastien Jodogne, University Hospital of Liege", - "Legal" : "Licensing information is available on http://www.orthanc-server.com/", - "Version" : "0.8.5" -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/SharedLibrary/SharedLibrary.cpp --- a/OrthancCppClient/SharedLibrary/SharedLibrary.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - - -#include "../../Core/HttpClient.h" -#include "../OrthancConnection.h" - - -class SharedLibrarySingleton -{ -public: - SharedLibrarySingleton() - { - Orthanc::HttpClient::GlobalInitialize(); - } - - ~SharedLibrarySingleton() - { - Orthanc::HttpClient::GlobalFinalize(); - } -}; - - -static SharedLibrarySingleton singleton_; - - -#include "AUTOGENERATED/ExternC.cpp" diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/Study.cpp --- a/OrthancCppClient/Study.cpp Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../Core/PrecompiledHeaders.h" -#include "Study.h" - -#include "OrthancConnection.h" - -namespace OrthancClient -{ - void Study::ReadStudy() - { - Orthanc::HttpClient client(connection_.GetHttpClient()); - client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/studies/" + id_); - - Json::Value v; - if (!client.Apply(study_)) - { - throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol); - } - } - - Orthanc::IDynamicObject* Study::GetFillerItem(size_t index) - { - Json::Value::ArrayIndex tmp = static_cast(index); - std::string id = study_["Series"][tmp].asString(); - return new Series(connection_, id.c_str()); - } - - Study::Study(const OrthancConnection& connection, - const char* id) : - connection_(connection), - id_(id), - series_(*this) - { - series_.SetThreadCount(connection.GetThreadCount()); - ReadStudy(); - } - - const char* Study::GetMainDicomTag(const char* tag, const char* defaultValue) const - { - if (study_["MainDicomTags"].isMember(tag)) - { - return study_["MainDicomTags"][tag].asCString(); - } - else - { - return defaultValue; - } - } -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancCppClient/Study.h --- a/OrthancCppClient/Study.h Wed Feb 11 10:40:08 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,119 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "Series.h" - -namespace OrthancClient -{ - /** - * {summary}{Connection to a study stored in %Orthanc.} - * {description}{This class encapsulates a connection to a study - * from a remote instance of %Orthanc.} - **/ - class LAAW_API Study : - public Orthanc::IDynamicObject, - private Orthanc::ArrayFilledByThreads::IFiller - { - private: - const OrthancConnection& connection_; - std::string id_; - Json::Value study_; - Orthanc::ArrayFilledByThreads series_; - - void ReadStudy(); - - virtual size_t GetFillerSize() - { - return study_["Series"].size(); - } - - virtual Orthanc::IDynamicObject* GetFillerItem(size_t index); - - public: - /** - * {summary}{Create a connection to some study.} - * {param}{connection The remote instance of %Orthanc.} - * {param}{id The %Orthanc identifier of the study.} - **/ - Study(const OrthancConnection& connection, - const char* id); - - /** - * {summary}{Reload the series of this study.} - * {description}{This method will reload the list of the series of this study. Pay attention to the fact that the series that have been previously returned by GetSeries() will be invalidated.} - **/ - void Reload() - { - series_.Reload(); - } - - /** - * {summary}{Return the number of series for this study.} - * {returns}{The number of series.} - **/ - uint32_t GetSeriesCount() - { - return series_.GetSize(); - } - - /** - * {summary}{Get some series of this study.} - * {description}{This method will return an object that contains information about some series. The series are indexed by a number between 0 (inclusive) and the result of GetSeriesCount() (exclusive).} - * {param}{index The index of the series of interest.} - * {returns}{The series.} - **/ - Series& GetSeries(uint32_t index) - { - return dynamic_cast(series_.GetItem(index)); - } - - /** - * {summary}{Get the %Orthanc identifier of this study.} - * {returns}{The identifier.} - **/ - const char* GetId() const - { - return id_.c_str(); - } - - /** - * {summary}{Get the value of one of the main DICOM tags for this study.} - * {param}{tag The name of the tag of interest ("StudyDate", "StudyDescription", "StudyInstanceUID" or "StudyTime").} - * {param}{defaultValue The default value to be returned if this tag does not exist.} - * {returns}{The value of the tag.} - **/ - const char* GetMainDicomTag(const char* tag, - const char* defaultValue) const; - }; -} diff -r 61ce8147f30d -r 4a5c79e31b60 OrthancExplorer/explorer.html --- a/OrthancExplorer/explorer.html Wed Feb 11 10:40:08 2015 +0100 +++ b/OrthancExplorer/explorer.html Wed Sep 30 13:23:31 2015 +0200 @@ -6,15 +6,15 @@ Orthanc Explorer - + - - + + @@ -30,6 +30,7 @@ + @@ -37,7 +38,10 @@

Find a patient

Plugins - Upload DICOM +
    @@ -75,7 +79,10 @@
    @@ -129,7 +136,10 @@ Study Patients - Upload DICOM +
    @@ -178,7 +188,10 @@ Patients - Upload DICOM +
    @@ -228,7 +241,10 @@ Instance Patients - Upload DICOM +
    @@ -284,15 +300,111 @@
    +
    +
    +

    DICOM Query/Retrieve (1/3)

    + Patients +
    +
    +
    +
    + + +
    + +
    +
    + Field of interest: + + + + + + + + +
    +
    + +
    + + +
    + +
    + + +
    + +
    +
    +
    + Modalities: + + + + + + + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + + +
    +
    +

    DICOM Query/Retrieve (2/3)

    + Patients + Query/Retrieve +
    +
    +
      +
    +
    +
    + + +
    +
    +

    DICOM Query/Retrieve (3/3)

    + Patients + Query/Retrieve +
    +
    +
      +
    +
    +
    + + +