Mercurial > hg > orthanc-dicomweb
changeset 104:4274441e21d4
rename Wado to WadoUri
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 25 Apr 2016 13:29:40 +0200 (2016-04-25) |
parents | cf5002bc63a8 |
children | e1e2b6b2139d 7cb164a4b5f0 |
files | CMakeLists.txt Orthanc/Core/ChunkedBuffer.cpp Orthanc/Core/Enumerations.cpp Orthanc/Core/Enumerations.h Orthanc/Core/Toolbox.cpp Orthanc/Core/Toolbox.h Orthanc/Resources/CMake/BoostConfiguration.cmake Orthanc/Resources/CMake/Compiler.cmake Orthanc/Resources/CMake/JsonCppConfiguration.cmake Plugin/Plugin.cpp Plugin/Wado.cpp Plugin/Wado.h Plugin/WadoUri.cpp Plugin/WadoUri.h |
diffstat | 14 files changed, 450 insertions(+), 332 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Thu Jan 28 17:09:48 2016 +0100 +++ b/CMakeLists.txt Mon Apr 25 13:29:40 2016 +0200 @@ -124,9 +124,9 @@ ${CMAKE_SOURCE_DIR}/Plugin/Plugin.cpp ${CMAKE_SOURCE_DIR}/Plugin/QidoRs.cpp ${CMAKE_SOURCE_DIR}/Plugin/StowRs.cpp - ${CMAKE_SOURCE_DIR}/Plugin/Wado.cpp ${CMAKE_SOURCE_DIR}/Plugin/WadoRs.cpp ${CMAKE_SOURCE_DIR}/Plugin/WadoRsRetrieveFrames.cpp + ${CMAKE_SOURCE_DIR}/Plugin/WadoUri.cpp ${AUTOGENERATED_SOURCES} )
--- a/Orthanc/Core/ChunkedBuffer.cpp Thu Jan 28 17:09:48 2016 +0100 +++ b/Orthanc/Core/ChunkedBuffer.cpp Mon Apr 25 13:29:40 2016 +0200 @@ -95,5 +95,6 @@ } chunks_.clear(); + numBytes_ = 0; } }
--- a/Orthanc/Core/Enumerations.cpp Thu Jan 28 17:09:48 2016 +0100 +++ b/Orthanc/Core/Enumerations.cpp Mon Apr 25 13:29:40 2016 +0200 @@ -212,10 +212,10 @@ 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"; + return "The TCP port of the HTTP server is privileged or already in use"; case ErrorCode_DicomPortInUse: - return "The TCP port of the DICOM server is already in use"; + return "The TCP port of the DICOM server is privileged or already in use"; case ErrorCode_BadHttpStatusInRest: return "This HTTP status is not allowed in a REST API"; @@ -328,6 +328,9 @@ case ErrorCode_NoWorklistHandler: return "No request handler factory for DICOM C-Find Modality SCP"; + case ErrorCode_AlreadyExistingTag: + return "Cannot override the value of a tag that already exists"; + default: if (error >= ErrorCode_START_PLUGINS) { @@ -718,6 +721,31 @@ } + const char* EnumerationToString(PixelFormat format) + { + switch (format) + { + case PixelFormat_RGB24: + return "RGB24"; + + case PixelFormat_RGBA32: + return "RGBA32"; + + case PixelFormat_Grayscale8: + return "Grayscale (unsigned 8bpp)"; + + case PixelFormat_Grayscale16: + return "Grayscale (unsigned 16bpp)"; + + case PixelFormat_SignedGrayscale16: + return "Grayscale (signed 16bpp)"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + Encoding StringToEncoding(const char* encoding) { std::string s(encoding);
--- a/Orthanc/Core/Enumerations.h Thu Jan 28 17:09:48 2016 +0100 +++ b/Orthanc/Core/Enumerations.h Mon Apr 25 13:29:40 2016 +0200 @@ -100,8 +100,8 @@ 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_HttpPortInUse = 2003 /*!< The TCP port of the HTTP server is privileged or already in use */, + ErrorCode_DicomPortInUse = 2004 /*!< The TCP port of the DICOM server is privileged or 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 */, @@ -139,6 +139,7 @@ ErrorCode_SslDisabled = 2039 /*!< Orthanc has been built without SSL support */, ErrorCode_CannotOrderSlices = 2040 /*!< Unable to order the slices of the series */, ErrorCode_NoWorklistHandler = 2041 /*!< No request handler factory for DICOM C-Find Modality SCP */, + ErrorCode_AlreadyExistingTag = 2042 /*!< Cannot override the value of a tag that already exists */, ErrorCode_START_PLUGINS = 1000000 }; @@ -444,6 +445,8 @@ const char* EnumerationToString(RequestOrigin origin); + const char* EnumerationToString(PixelFormat format); + Encoding StringToEncoding(const char* encoding); ResourceType StringToResourceType(const char* type);
--- a/Orthanc/Core/Toolbox.cpp Thu Jan 28 17:09:48 2016 +0100 +++ b/Orthanc/Core/Toolbox.cpp Mon Apr 25 13:29:40 2016 +0200 @@ -67,7 +67,7 @@ #include <limits.h> /* PATH_MAX */ #endif -#if defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) +#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) #include <limits.h> /* PATH_MAX */ #include <signal.h> #include <unistd.h> @@ -132,7 +132,7 @@ { #if defined(_WIN32) ::Sleep(static_cast<DWORD>(microSeconds / static_cast<uint64_t>(1000))); -#elif defined(__linux) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) usleep(microSeconds); #else #error Support your platform here @@ -206,6 +206,17 @@ } + static std::streamsize GetStreamSize(std::istream& f) + { + // http://www.cplusplus.com/reference/iostream/istream/tellg/ + f.seekg(0, std::ios::end); + std::streamsize size = f.tellg(); + f.seekg(0, std::ios::beg); + + return size; + } + + void Toolbox::ReadFile(std::string& content, const std::string& path) { @@ -222,11 +233,7 @@ throw OrthancException(ErrorCode_InexistentFile); } - // http://www.cplusplus.com/reference/iostream/istream/tellg/ - f.seekg(0, std::ios::end); - std::streamsize size = f.tellg(); - f.seekg(0, std::ios::beg); - + std::streamsize size = GetStreamSize(f); content.resize(size); if (size != 0) { @@ -237,6 +244,51 @@ } + bool Toolbox::ReadHeader(std::string& header, + const std::string& path, + size_t headerSize) + { + if (!IsRegularFile(path)) + { + LOG(ERROR) << std::string("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()) + { + throw OrthancException(ErrorCode_InexistentFile); + } + + bool full = true; + + { + std::streamsize size = GetStreamSize(f); + if (size <= 0) + { + headerSize = 0; + full = false; + } + else if (static_cast<size_t>(size) < headerSize) + { + headerSize = size; // Truncate to the size of the file + full = false; + } + } + + header.resize(headerSize); + if (headerSize != 0) + { + f.read(reinterpret_cast<char*>(&header[0]), headerSize); + } + + f.close(); + + return full; + } + + void Toolbox::WriteFile(const void* content, size_t size, const std::string& path) @@ -535,7 +587,7 @@ # if BOOST_HAS_REGEX == 1 - void Toolbox::DecodeDataUriScheme(std::string& mime, + bool Toolbox::DecodeDataUriScheme(std::string& mime, std::string& content, const std::string& source) { @@ -547,10 +599,11 @@ { mime = what[1]; DecodeBase64(content, what[2]); + return true; } else { - throw OrthancException(ErrorCode_BadFileFormat); + return false; } } # endif @@ -577,7 +630,7 @@ return std::string(&buffer[0]); } -#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) +#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) static std::string GetPathToExecutableInternal() { std::vector<char> buffer(PATH_MAX + 1);
--- a/Orthanc/Core/Toolbox.h Thu Jan 28 17:09:48 2016 +0100 +++ b/Orthanc/Core/Toolbox.h Mon Apr 25 13:29:40 2016 +0200 @@ -66,6 +66,10 @@ void ReadFile(std::string& content, const std::string& path); + bool ReadHeader(std::string& header, + const std::string& path, + size_t headerSize); + void WriteFile(const std::string& content, const std::string& path); @@ -123,7 +127,7 @@ const std::string& data); # if BOOST_HAS_REGEX == 1 - void DecodeDataUriScheme(std::string& mime, + bool DecodeDataUriScheme(std::string& mime, std::string& content, const std::string& source); # endif
--- a/Orthanc/Resources/CMake/BoostConfiguration.cmake Thu Jan 28 17:09:48 2016 +0100 +++ b/Orthanc/Resources/CMake/BoostConfiguration.cmake Mon Apr 25 13:29:40 2016 +0200 @@ -8,7 +8,7 @@ #set(Boost_USE_STATIC_LIBS ON) find_package(Boost - COMPONENTS filesystem thread system date_time regex locale) + COMPONENTS filesystem thread system date_time regex locale ${ORTHANC_BOOST_COMPONENTS}) if (NOT Boost_FOUND) message(FATAL_ERROR "Unable to locate Boost on this system") @@ -39,10 +39,10 @@ if (BOOST_STATIC) - # Parameters for Boost 1.59.0 - set(BOOST_NAME boost_1_59_0) - set(BOOST_BCP_SUFFIX bcpdigest-0.9.5) - set(BOOST_MD5 "08abb7cdbea0b380f9ab0d5cce476f12") + # Parameters for Boost 1.60.0 + set(BOOST_NAME boost_1_60_0) + set(BOOST_BCP_SUFFIX bcpdigest-1.0.1) + set(BOOST_MD5 "0646971514a1e012fbe382c5662a8605") set(BOOST_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/${BOOST_NAME}_${BOOST_BCP_SUFFIX}.tar.gz") set(BOOST_FILESYSTEM_SOURCES_DIR "${BOOST_NAME}/libs/filesystem/src") set(BOOST_SOURCES_DIR ${CMAKE_BINARY_DIR}/${BOOST_NAME})
--- a/Orthanc/Resources/CMake/Compiler.cmake Thu Jan 28 17:09:48 2016 +0100 +++ b/Orthanc/Resources/CMake/Compiler.cmake Mon Apr 25 13:29:40 2016 +0200 @@ -10,7 +10,7 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-long-long -Wno-implicit-function-declaration") # --std=c99 makes libcurl not to compile # -pedantic gives a lot of warnings on OpenSSL - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wno-long-long -Wno-variadic-macros") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -Wno-variadic-macros") if (CMAKE_CROSSCOMPILING) # http://stackoverflow.com/a/3543845/881731 @@ -73,6 +73,11 @@ link_libraries(dl) endif() + CHECK_INCLUDE_FILES(uuid/uuid.h HAVE_UUID_H) + if (NOT HAVE_UUID_H) + message(FATAL_ERROR "Please install the uuid-dev package") + endif() + elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") if (MSVC) message("MSVC compiler version = " ${MSVC_VERSION} "\n") @@ -97,6 +102,11 @@ link_libraries(rpcrt4 ws2_32) if (CMAKE_COMPILER_IS_GNUCXX) + # Some additional C/C++ compiler flags for MinGW + SET(MINGW_NO_WARNINGS "-Wno-unused-function -Wno-unused-variable") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MINGW_NO_WARNINGS} -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MINGW_NO_WARNINGS}") + # This is a patch for MinGW64 SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++") SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++") @@ -120,6 +130,11 @@ ) link_libraries(iconv) + CHECK_INCLUDE_FILES(uuid/uuid.h HAVE_UUID_H) + if (NOT HAVE_UUID_H) + message(FATAL_ERROR "Please install the uuid-dev package") + endif() + endif() @@ -139,17 +154,6 @@ endif() -if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") - CHECK_INCLUDE_FILES(rpc.h HAVE_UUID_H) -else() - CHECK_INCLUDE_FILES(uuid/uuid.h HAVE_UUID_H) -endif() - -if (NOT HAVE_UUID_H) - message(FATAL_ERROR "Please install the uuid-dev package") -endif() - - if (STATIC_BUILD) add_definitions(-DORTHANC_STATIC=1) else()
--- a/Orthanc/Resources/CMake/JsonCppConfiguration.cmake Thu Jan 28 17:09:48 2016 +0100 +++ b/Orthanc/Resources/CMake/JsonCppConfiguration.cmake Mon Apr 25 13:29:40 2016 +0200 @@ -32,4 +32,29 @@ message(FATAL_ERROR "Please install the libjsoncpp-dev package") endif() + # Switch to the C++11 standard if the version of JsonCpp is 1.y.z + if (EXISTS ${JSONCPP_INCLUDE_DIR}/json/version.h) + file(STRINGS + "${JSONCPP_INCLUDE_DIR}/json/version.h" + JSONCPP_VERSION_MAJOR1 REGEX + ".*define JSONCPP_VERSION_MAJOR.*") + + if (NOT JSONCPP_VERSION_MAJOR1) + message(FATAL_ERROR "Unable to extract the major version of JsonCpp") + endif() + + string(REGEX REPLACE + ".*JSONCPP_VERSION_MAJOR.*([0-9]+)$" "\\1" + JSONCPP_VERSION_MAJOR ${JSONCPP_VERSION_MAJOR1}) + message("JsonCpp major version: ${JSONCPP_VERSION_MAJOR}") + + if (CMAKE_COMPILER_IS_GNUCXX AND + JSONCPP_VERSION_MAJOR GREATER 0) + message("Switching to C++11 standard, as version of JsonCpp is >= 1.0.0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-deprecated-declarations") + endif() + else() + message("Unable to detect the major version of JsonCpp, assuming < 1.0.0") + endif() + endif()
--- a/Plugin/Plugin.cpp Thu Jan 28 17:09:48 2016 +0100 +++ b/Plugin/Plugin.cpp Mon Apr 25 13:29:40 2016 +0200 @@ -23,7 +23,7 @@ #include "QidoRs.h" #include "StowRs.h" #include "WadoRs.h" -#include "Wado.h" +#include "WadoUri.h" #include "Configuration.h" @@ -222,7 +222,7 @@ std::string message = "URI to the WADO API: " + wado; OrthancPluginLogWarning(context_, message.c_str()); - OrthancPluginRegisterRestCallback(context_, wado.c_str(), Protect<WadoCallback>); + OrthancPluginRegisterRestCallback(context_, wado.c_str(), Protect<WadoUriCallback>); } else {
--- a/Plugin/Wado.cpp Thu Jan 28 17:09:48 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,270 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 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 Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#include "Wado.h" -#include "Plugin.h" - -#include "../Orthanc/Core/OrthancException.h" -#include "Configuration.h" - -#include <string> - -static bool MapWadoToOrthancIdentifier(std::string& orthanc, - char* (*func) (OrthancPluginContext*, const char*), - const std::string& dicom) -{ - char* tmp = func(context_, dicom.c_str()); - - if (tmp) - { - orthanc = tmp; - OrthancPluginFreeString(context_, tmp); - return true; - } - else - { - return false; - } -} - - -static bool LocateInstance(std::string& instance, - std::string& contentType, - const OrthancPluginHttpRequest* request) -{ - std::string requestType, studyUid, seriesUid, objectUid; - - for (uint32_t i = 0; i < request->getCount; i++) - { - std::string key(request->getKeys[i]); - std::string value(request->getValues[i]); - - if (key == "studyUID") - { - studyUid = value; - } - else if (key == "seriesUID") - { - seriesUid = value; - } - else if (key == "objectUID") // In WADO, "objectUID" corresponds to "SOPInstanceUID" - { - objectUid = value; - } - else if (key == "requestType") - { - requestType = value; - } - else if (key == "contentType") - { - contentType = value; - } - } - - if (requestType != "WADO") - { - std::string msg = "WADO: Invalid requestType: \"" + requestType + "\""; - OrthancPluginLogError(context_, msg.c_str()); - return false; - } - - if (objectUid.empty()) - { - OrthancPluginLogError(context_, "WADO: No SOPInstanceUID provided"); - return false; - } - - if (!MapWadoToOrthancIdentifier(instance, OrthancPluginLookupInstance, objectUid)) - { - std::string msg = "WADO: No such SOPInstanceUID in Orthanc: \"" + objectUid + "\""; - OrthancPluginLogError(context_, msg.c_str()); - return false; - } - - /** - * Below are only sanity checks to ensure that the possibly provided - * "seriesUID" and "studyUID" match that of the provided instance. - **/ - - if (!seriesUid.empty()) - { - std::string series; - if (!MapWadoToOrthancIdentifier(series, OrthancPluginLookupSeries, seriesUid)) - { - std::string msg = "WADO: No such SeriesInstanceUID in Orthanc: \"" + seriesUid + "\""; - OrthancPluginLogError(context_, msg.c_str()); - return false; - } - else - { - Json::Value info; - if (!OrthancPlugins::RestApiGetJson(info, context_, "/instances/" + instance + "/series") || - info["MainDicomTags"]["SeriesInstanceUID"] != seriesUid) - { - std::string msg = "WADO: Instance " + objectUid + " does not belong to series " + seriesUid; - OrthancPluginLogError(context_, msg.c_str()); - return false; - } - } - } - - if (!studyUid.empty()) - { - std::string study; - if (!MapWadoToOrthancIdentifier(study, OrthancPluginLookupStudy, studyUid)) - { - std::string msg = "WADO: No such StudyInstanceUID in Orthanc: \"" + studyUid + "\""; - OrthancPluginLogError(context_, msg.c_str()); - return false; - } - else - { - Json::Value info; - if (!OrthancPlugins::RestApiGetJson(info, context_, "/instances/" + instance + "/study") || - info["MainDicomTags"]["StudyInstanceUID"] != studyUid) - { - std::string msg = "WADO: Instance " + objectUid + " does not belong to study " + studyUid; - OrthancPluginLogError(context_, msg.c_str()); - return false; - } - } - } - - return true; -} - - -static void AnswerDicom(OrthancPluginRestOutput* output, - const std::string& instance) -{ - std::string uri = "/instances/" + instance + "/file"; - - std::string dicom; - if (OrthancPlugins::RestApiGetString(dicom, context_, uri)) - { - OrthancPluginAnswerBuffer(context_, output, dicom.c_str(), dicom.size(), "application/dicom"); - } - else - { - std::string msg = "WADO: Unable to retrieve DICOM file from " + uri; - OrthancPluginLogError(context_, msg.c_str()); - throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin); - } -} - - -static bool RetrievePngPreview(std::string& png, - const std::string& instance) -{ - std::string uri = "/instances/" + instance + "/preview"; - - if (OrthancPlugins::RestApiGetString(png, context_, uri, true)) - { - return true; - } - else - { - std::string msg = "WADO: Unable to generate a preview image for " + uri; - OrthancPluginLogError(context_, msg.c_str()); - return false; - } -} - - -static void AnswerPngPreview(OrthancPluginRestOutput* output, - const std::string& instance) -{ - std::string png; - if (RetrievePngPreview(png, instance)) - { - OrthancPluginAnswerBuffer(context_, output, png.c_str(), png.size(), "image/png"); - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin); - } -} - - -static void AnswerJpegPreview(OrthancPluginRestOutput* output, - const std::string& instance) -{ - // Retrieve the preview in the PNG format - std::string png; - if (!RetrievePngPreview(png, instance)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin); - } - - // Decode the PNG file - OrthancPluginImage* image = OrthancPluginUncompressImage( - context_, png.c_str(), png.size(), OrthancPluginImageFormat_Png); - - // Convert to JPEG - OrthancPluginCompressAndAnswerJpegImage( - context_, output, - OrthancPluginGetImagePixelFormat(context_, image), - OrthancPluginGetImageWidth(context_, image), - OrthancPluginGetImageHeight(context_, image), - OrthancPluginGetImagePitch(context_, image), - OrthancPluginGetImageBuffer(context_, image), - 90 /*quality*/); - - OrthancPluginFreeImage(context_, image); -} - - -void WadoCallback(OrthancPluginRestOutput* output, - const char* url, - const OrthancPluginHttpRequest* request) -{ - if (request->method != OrthancPluginHttpMethod_Get) - { - OrthancPluginSendMethodNotAllowed(context_, output, "GET"); - return; - } - - std::string instance; - std::string contentType = "image/jpg"; // By default, JPEG image will be returned - if (!LocateInstance(instance, contentType, request)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); - } - - if (contentType == "application/dicom") - { - AnswerDicom(output, instance); - } - else if (contentType == "image/png") - { - AnswerPngPreview(output, instance); - } - else if (contentType == "image/jpeg" || - contentType == "image/jpg") - { - AnswerJpegPreview(output, instance); - } - else - { - std::string msg = "WADO: Unsupported content type: \"" + contentType + "\""; - OrthancPluginLogError(context_, msg.c_str()); - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest); - } -}
--- a/Plugin/Wado.h Thu Jan 28 17:09:48 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 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 Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "Configuration.h" - -void WadoCallback(OrthancPluginRestOutput* output, - const char* url, - const OrthancPluginHttpRequest* request);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugin/WadoUri.cpp Mon Apr 25 13:29:40 2016 +0200 @@ -0,0 +1,270 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 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 Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "WadoUri.h" +#include "Plugin.h" + +#include "../Orthanc/Core/OrthancException.h" +#include "Configuration.h" + +#include <string> + +static bool MapWadoToOrthancIdentifier(std::string& orthanc, + char* (*func) (OrthancPluginContext*, const char*), + const std::string& dicom) +{ + char* tmp = func(context_, dicom.c_str()); + + if (tmp) + { + orthanc = tmp; + OrthancPluginFreeString(context_, tmp); + return true; + } + else + { + return false; + } +} + + +static bool LocateInstance(std::string& instance, + std::string& contentType, + const OrthancPluginHttpRequest* request) +{ + std::string requestType, studyUid, seriesUid, objectUid; + + for (uint32_t i = 0; i < request->getCount; i++) + { + std::string key(request->getKeys[i]); + std::string value(request->getValues[i]); + + if (key == "studyUID") + { + studyUid = value; + } + else if (key == "seriesUID") + { + seriesUid = value; + } + else if (key == "objectUID") // In WADO-URI, "objectUID" corresponds to "SOPInstanceUID" + { + objectUid = value; + } + else if (key == "requestType") + { + requestType = value; + } + else if (key == "contentType") + { + contentType = value; + } + } + + if (requestType != "WADO") + { + std::string msg = "WADO-URI: Invalid requestType: \"" + requestType + "\""; + OrthancPluginLogError(context_, msg.c_str()); + return false; + } + + if (objectUid.empty()) + { + OrthancPluginLogError(context_, "WADO-URI: No SOPInstanceUID provided"); + return false; + } + + if (!MapWadoToOrthancIdentifier(instance, OrthancPluginLookupInstance, objectUid)) + { + std::string msg = "WADO-URI: No such SOPInstanceUID in Orthanc: \"" + objectUid + "\""; + OrthancPluginLogError(context_, msg.c_str()); + return false; + } + + /** + * Below are only sanity checks to ensure that the possibly provided + * "seriesUID" and "studyUID" match that of the provided instance. + **/ + + if (!seriesUid.empty()) + { + std::string series; + if (!MapWadoToOrthancIdentifier(series, OrthancPluginLookupSeries, seriesUid)) + { + std::string msg = "WADO-URI: No such SeriesInstanceUID in Orthanc: \"" + seriesUid + "\""; + OrthancPluginLogError(context_, msg.c_str()); + return false; + } + else + { + Json::Value info; + if (!OrthancPlugins::RestApiGetJson(info, context_, "/instances/" + instance + "/series") || + info["MainDicomTags"]["SeriesInstanceUID"] != seriesUid) + { + std::string msg = "WADO-URI: Instance " + objectUid + " does not belong to series " + seriesUid; + OrthancPluginLogError(context_, msg.c_str()); + return false; + } + } + } + + if (!studyUid.empty()) + { + std::string study; + if (!MapWadoToOrthancIdentifier(study, OrthancPluginLookupStudy, studyUid)) + { + std::string msg = "WADO-URI: No such StudyInstanceUID in Orthanc: \"" + studyUid + "\""; + OrthancPluginLogError(context_, msg.c_str()); + return false; + } + else + { + Json::Value info; + if (!OrthancPlugins::RestApiGetJson(info, context_, "/instances/" + instance + "/study") || + info["MainDicomTags"]["StudyInstanceUID"] != studyUid) + { + std::string msg = "WADO-URI: Instance " + objectUid + " does not belong to study " + studyUid; + OrthancPluginLogError(context_, msg.c_str()); + return false; + } + } + } + + return true; +} + + +static void AnswerDicom(OrthancPluginRestOutput* output, + const std::string& instance) +{ + std::string uri = "/instances/" + instance + "/file"; + + std::string dicom; + if (OrthancPlugins::RestApiGetString(dicom, context_, uri)) + { + OrthancPluginAnswerBuffer(context_, output, dicom.c_str(), dicom.size(), "application/dicom"); + } + else + { + std::string msg = "WADO-URI: Unable to retrieve DICOM file from " + uri; + OrthancPluginLogError(context_, msg.c_str()); + throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin); + } +} + + +static bool RetrievePngPreview(std::string& png, + const std::string& instance) +{ + std::string uri = "/instances/" + instance + "/preview"; + + if (OrthancPlugins::RestApiGetString(png, context_, uri, true)) + { + return true; + } + else + { + std::string msg = "WADO-URI: Unable to generate a preview image for " + uri; + OrthancPluginLogError(context_, msg.c_str()); + return false; + } +} + + +static void AnswerPngPreview(OrthancPluginRestOutput* output, + const std::string& instance) +{ + std::string png; + if (RetrievePngPreview(png, instance)) + { + OrthancPluginAnswerBuffer(context_, output, png.c_str(), png.size(), "image/png"); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin); + } +} + + +static void AnswerJpegPreview(OrthancPluginRestOutput* output, + const std::string& instance) +{ + // Retrieve the preview in the PNG format + std::string png; + if (!RetrievePngPreview(png, instance)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin); + } + + // Decode the PNG file + OrthancPluginImage* image = OrthancPluginUncompressImage( + context_, png.c_str(), png.size(), OrthancPluginImageFormat_Png); + + // Convert to JPEG + OrthancPluginCompressAndAnswerJpegImage( + context_, output, + OrthancPluginGetImagePixelFormat(context_, image), + OrthancPluginGetImageWidth(context_, image), + OrthancPluginGetImageHeight(context_, image), + OrthancPluginGetImagePitch(context_, image), + OrthancPluginGetImageBuffer(context_, image), + 90 /*quality*/); + + OrthancPluginFreeImage(context_, image); +} + + +void WadoUriCallback(OrthancPluginRestOutput* output, + const char* url, + const OrthancPluginHttpRequest* request) +{ + if (request->method != OrthancPluginHttpMethod_Get) + { + OrthancPluginSendMethodNotAllowed(context_, output, "GET"); + return; + } + + std::string instance; + std::string contentType = "image/jpg"; // By default, JPEG image will be returned + if (!LocateInstance(instance, contentType, request)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); + } + + if (contentType == "application/dicom") + { + AnswerDicom(output, instance); + } + else if (contentType == "image/png") + { + AnswerPngPreview(output, instance); + } + else if (contentType == "image/jpeg" || + contentType == "image/jpg") + { + AnswerJpegPreview(output, instance); + } + else + { + std::string msg = "WADO-URI: Unsupported content type: \"" + contentType + "\""; + OrthancPluginLogError(context_, msg.c_str()); + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugin/WadoUri.h Mon Apr 25 13:29:40 2016 +0200 @@ -0,0 +1,27 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 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 Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "Configuration.h" + +void WadoUriCallback(OrthancPluginRestOutput* output, + const char* url, + const OrthancPluginHttpRequest* request);