# HG changeset patch # User Sebastien Jodogne # Date 1623937641 -7200 # Node ID dd6274412ff4aec3853bbb1f2876ced65a791f40 # Parent 651f069c7f77ff6cfe17012522d42702a31acc58 new configuration option "ExternalDictionaries" to load external DICOM dictionaries diff -r 651f069c7f77 -r dd6274412ff4 NEWS --- a/NEWS Thu Jun 17 14:00:34 2021 +0200 +++ b/NEWS Thu Jun 17 15:47:21 2021 +0200 @@ -5,7 +5,9 @@ ------- * Orthanc now anonymizes according to Basic Profile of PS 3.15-2021b Table E.1-1 -* New configuration option "SynchronousZipStream" to disable streaming of ZIP +* New configuration options: + - "ExternalDictionaries" to load external DICOM dictionaries (useful for DICONDE) + - "SynchronousZipStream" to disable streaming of ZIP REST API -------- diff -r 651f069c7f77 -r dd6274412ff4 OrthancFramework/Sources/DicomFormat/DicomTag.cpp --- a/OrthancFramework/Sources/DicomFormat/DicomTag.cpp Thu Jun 17 14:00:34 2021 +0200 +++ b/OrthancFramework/Sources/DicomFormat/DicomTag.cpp Thu Jun 17 15:47:21 2021 +0200 @@ -183,61 +183,6 @@ } - const char* DicomTag::GetMainTagsName() const - { - if (*this == DICOM_TAG_ACCESSION_NUMBER) - return "AccessionNumber"; - - if (*this == DICOM_TAG_SOP_INSTANCE_UID) - return "SOPInstanceUID"; - - if (*this == DICOM_TAG_PATIENT_ID) - return "PatientID"; - - if (*this == DICOM_TAG_SERIES_INSTANCE_UID) - return "SeriesInstanceUID"; - - if (*this == DICOM_TAG_STUDY_INSTANCE_UID) - return "StudyInstanceUID"; - - if (*this == DICOM_TAG_PIXEL_DATA) - return "PixelData"; - - if (*this == DICOM_TAG_IMAGE_INDEX) - return "ImageIndex"; - - if (*this == DICOM_TAG_INSTANCE_NUMBER) - return "InstanceNumber"; - - if (*this == DICOM_TAG_NUMBER_OF_SLICES) - return "NumberOfSlices"; - - if (*this == DICOM_TAG_NUMBER_OF_FRAMES) - return "NumberOfFrames"; - - if (*this == DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES) - return "CardiacNumberOfImages"; - - if (*this == DICOM_TAG_IMAGES_IN_ACQUISITION) - return "ImagesInAcquisition"; - - if (*this == DICOM_TAG_PATIENT_NAME) - return "PatientName"; - - if (*this == DICOM_TAG_IMAGE_POSITION_PATIENT) - return "ImagePositionPatient"; - - if (*this == DICOM_TAG_IMAGE_ORIENTATION_PATIENT) - return "ImageOrientationPatient"; - - // New in Orthanc 1.6.0, as tagged as "RETIRED_" since DCMTK 3.6.4 - if (*this == DICOM_TAG_OTHER_PATIENT_IDS) - return "OtherPatientIDs"; - - return ""; - } - - void DicomTag::AddTagsForModule(std::set& target, DicomModule module) { diff -r 651f069c7f77 -r dd6274412ff4 OrthancFramework/Sources/DicomFormat/DicomTag.h --- a/OrthancFramework/Sources/DicomFormat/DicomTag.h Thu Jun 17 14:00:34 2021 +0200 +++ b/OrthancFramework/Sources/DicomFormat/DicomTag.h Thu Jun 17 15:47:21 2021 +0200 @@ -49,8 +49,6 @@ bool IsPrivate() const; - const char* GetMainTagsName() const; - bool operator< (const DicomTag& other) const; bool operator<= (const DicomTag& other) const; diff -r 651f069c7f77 -r dd6274412ff4 OrthancFramework/Sources/DicomParsing/DicomModification.cpp --- a/OrthancFramework/Sources/DicomParsing/DicomModification.cpp Thu Jun 17 14:00:34 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/DicomModification.cpp Thu Jun 17 15:47:21 2021 +0200 @@ -804,14 +804,16 @@ ValueRepresentation vr = FromDcmtkBridge::LookupValueRepresentation(*it); if (*it == DICOM_TAG_PATIENT_ID) { - if (vr != ValueRepresentation_LongString) + if (vr != ValueRepresentation_LongString && + vr != ValueRepresentation_NotSupported /* if no dictionary loaded */) { throw OrthancException(ErrorCode_InternalError); } } else if (*it == DICOM_TAG_PATIENT_NAME) { - if (vr != ValueRepresentation_PersonName) + if (vr != ValueRepresentation_PersonName && + vr != ValueRepresentation_NotSupported /* if no dictionary loaded */) { throw OrthancException(ErrorCode_InternalError); } diff -r 651f069c7f77 -r dd6274412ff4 OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp --- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Thu Jun 17 14:00:34 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Thu Jun 17 15:47:21 2021 +0200 @@ -117,6 +117,9 @@ #endif +static bool hasExternalDictionaries_ = false; + + namespace Orthanc { static bool IsBinaryTag(const DcmTag& key) @@ -226,22 +229,22 @@ } -#define DCMTK_TO_CTYPE_CONVERTER(converter, cType, dcmtkType, getter, toStringFunction) \ +#define DCMTK_TO_CTYPE_CONVERTER(converter, cType, dcmtkType, getter, toStringFunction) \ \ struct converter \ { \ typedef cType CType; \ \ ORTHANC_FORCE_INLINE \ - static bool Apply(CType& result, \ - DcmElement& element, \ - size_t i) \ + static bool Apply(CType& result, \ + DcmElement& element, \ + size_t i) \ { \ return dynamic_cast(element).getter(result, i).good(); \ } \ \ ORTHANC_FORCE_INLINE \ - static std::string ToString(CType value) \ + static std::string ToString(CType value) \ { \ return toStringFunction(value); \ } \ @@ -285,15 +288,15 @@ void FromDcmtkBridge::InitializeDictionary(bool loadPrivateDictionary) { - LOG(INFO) << "Using DCTMK version: " << DCMTK_VERSION_NUMBER; + CLOG(INFO, DICOM) << "Using DCTMK version: " << DCMTK_VERSION_NUMBER; +#if DCMTK_USE_EMBEDDED_DICTIONARIES == 1 { DictionaryLocker locker; locker->clear(); -#if DCMTK_USE_EMBEDDED_DICTIONARIES == 1 - LOG(INFO) << "Loading the embedded dictionaries"; + CLOG(INFO, DICOM) << "Loading the embedded dictionaries"; /** * Do not load DICONDE dictionary, it breaks the other tags. The * command "strace storescu 2>&1 |grep dic" shows that DICONDE @@ -305,15 +308,16 @@ if (loadPrivateDictionary) { - LOG(INFO) << "Loading the embedded dictionary of private tags"; + CLOG(INFO, DICOM) << "Loading the embedded dictionary of private tags"; LoadEmbeddedDictionary(*locker, FrameworkResources::DICTIONARY_PRIVATE); } else { - LOG(INFO) << "The dictionary of private tags has not been loaded"; + CLOG(INFO, DICOM) << "The dictionary of private tags has not been loaded"; } - + } #else + { std::vector dictionaries; const char* env = std::getenv(DCM_DICT_ENVIRONMENT_VARIABLE); @@ -331,21 +335,17 @@ { boost::filesystem::path base = DCMTK_DICTIONARY_DIR; dictionaries.push_back((base / "dicom.dic").string()); - dictionaries.push_back((base / "private.dic").string()); - } - - for (size_t i = 0; i < dictionaries.size(); i++) - { - LOG(WARNING) << "Loading external DICOM dictionary: \"" << dictionaries[i] << "\""; - - if (!locker->loadDictionary(dictionaries[i].c_str())) + + if (loadPrivateDictionary) { - throw OrthancException(ErrorCode_InexistentFile); + dictionaries.push_back((base / "private.dic").string()); } } + LoadExternalDictionaries(dictionaries); + hasExternalDictionaries_ = false; // Fix the side-effect of "LoadExternalDictionaries()" + } #endif - } /* make sure data dictionary is loaded */ if (!dcmDataDict.isDictionaryLoaded()) @@ -367,6 +367,27 @@ } + void FromDcmtkBridge::LoadExternalDictionaries(const std::vector& dictionaries) + { + DictionaryLocker locker; + + CLOG(INFO, DICOM) << "Clearing the DICOM dictionary"; + locker->clear(); + + for (size_t i = 0; i < dictionaries.size(); i++) + { + LOG(WARNING) << "Loading external DICOM dictionary: \"" << dictionaries[i] << "\""; + + if (!locker->loadDictionary(dictionaries[i].c_str())) + { + throw OrthancException(ErrorCode_InexistentFile); + } + } + + hasExternalDictionaries_ = true; + } + + void FromDcmtkBridge::RegisterDictionaryTag(const DicomTag& tag, ValueRepresentation vr, const std::string& name, @@ -392,10 +413,10 @@ DcmEVR evr = ToDcmtkBridge::Convert(vr); - LOG(INFO) << "Registering tag in dictionary: (" << tag.Format() << ") " - << (DcmVR(evr).getValidVRName()) << " " - << name << " (multiplicity: " << minMultiplicity << "-" - << (arbitrary ? "n" : boost::lexical_cast(maxMultiplicity)) << ")"; + CLOG(INFO, DICOM) << "Registering tag in dictionary: (" << tag.Format() << ") " + << (DcmVR(evr).getValidVRName()) << " " + << name << " (multiplicity: " << minMultiplicity << "-" + << (arbitrary ? "n" : boost::lexical_cast(maxMultiplicity)) << ")"; std::unique_ptr entry; if (privateCreator.empty()) @@ -983,7 +1004,7 @@ { // The "0" below lets "LeafValueToJson()" take care of "TooLong" values std::unique_ptr v(FromDcmtkBridge::ConvertLeafElement - (element, flags, 0, encoding, hasCodeExtensions, ignoreTagLength)); + (element, flags, 0, encoding, hasCodeExtensions, ignoreTagLength)); if (ignoreTagLength.find(GetTag(element)) == ignoreTagLength.end()) { @@ -1113,18 +1134,69 @@ } - static std::string GetTagNameInternal(DcmTag& tag) { + if (!hasExternalDictionaries_) { - // Some patches for important tags because of different DICOM - // dictionaries between DCMTK versions + /** + * Some patches for important tags because of different DICOM + * dictionaries between DCMTK versions. Since Orthanc 1.9.4, we + * don't apply these patches if external dictionaries are + * loaded, notably for compatibility with DICONDE. In Orthanc <= + * 1.9.3, this was done by method "DicomTag::GetMainTagsName()". + **/ + DicomTag tmp(tag.getGroup(), tag.getElement()); - std::string n = tmp.GetMainTagsName(); - if (n.size() != 0) - { - return n; - } + + if (tmp == DICOM_TAG_ACCESSION_NUMBER) + return "AccessionNumber"; + + if (tmp == DICOM_TAG_SOP_INSTANCE_UID) + return "SOPInstanceUID"; + + if (tmp == DICOM_TAG_PATIENT_ID) + return "PatientID"; + + if (tmp == DICOM_TAG_SERIES_INSTANCE_UID) + return "SeriesInstanceUID"; + + if (tmp == DICOM_TAG_STUDY_INSTANCE_UID) + return "StudyInstanceUID"; + + if (tmp == DICOM_TAG_PIXEL_DATA) + return "PixelData"; + + if (tmp == DICOM_TAG_IMAGE_INDEX) + return "ImageIndex"; + + if (tmp == DICOM_TAG_INSTANCE_NUMBER) + return "InstanceNumber"; + + if (tmp == DICOM_TAG_NUMBER_OF_SLICES) + return "NumberOfSlices"; + + if (tmp == DICOM_TAG_NUMBER_OF_FRAMES) + return "NumberOfFrames"; + + if (tmp == DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES) + return "CardiacNumberOfImages"; + + if (tmp == DICOM_TAG_IMAGES_IN_ACQUISITION) + return "ImagesInAcquisition"; + + if (tmp == DICOM_TAG_PATIENT_NAME) + return "PatientName"; + + if (tmp == DICOM_TAG_IMAGE_POSITION_PATIENT) + return "ImagePositionPatient"; + + if (tmp == DICOM_TAG_IMAGE_ORIENTATION_PATIENT) + return "ImageOrientationPatient"; + + // New in Orthanc 1.6.0, as tagged as "RETIRED_" since DCMTK 3.6.4 + if (tmp == DICOM_TAG_OTHER_PATIENT_IDS) + return "OtherPatientIDs"; + // End of patches } @@ -1217,7 +1289,7 @@ } else { - LOG(INFO) << "Unknown DICOM tag: \"" << name << "\""; + CLOG(INFO, DICOM) << "Unknown DICOM tag: \"" << name << "\""; throw OrthancException(ErrorCode_UnknownDicomTag); } #endif @@ -1441,14 +1513,14 @@ if (known) { - LOG(INFO) << "Transcoded an image from transfer syntax " - << GetTransferSyntaxUid(sourceSyntax) << " to " - << GetTransferSyntaxUid(syntax); + CLOG(INFO, DICOM) << "Transcoded an image from transfer syntax " + << GetTransferSyntaxUid(sourceSyntax) << " to " + << GetTransferSyntaxUid(syntax); } else { - LOG(INFO) << "Transcoded an image from unknown transfer syntax to " - << GetTransferSyntaxUid(syntax); + CLOG(INFO, DICOM) << "Transcoded an image from unknown transfer syntax to " + << GetTransferSyntaxUid(syntax); } return true; @@ -1507,16 +1579,16 @@ return ValueRepresentation_OtherByte; #if DCMTK_VERSION_NUMBER >= 361 - case EVR_OD: - return ValueRepresentation_OtherDouble; + case EVR_OD: + return ValueRepresentation_OtherDouble; #endif case EVR_OF: return ValueRepresentation_OtherFloat; #if DCMTK_VERSION_NUMBER >= 362 - case EVR_OL: - return ValueRepresentation_OtherLong; + case EVR_OL: + return ValueRepresentation_OtherLong; #endif case EVR_OW: @@ -1695,9 +1767,9 @@ throw OrthancException(ErrorCode_ParameterOutOfRange); - /** - * String types. - **/ + /** + * String types. + **/ case EVR_DS: // decimal string case EVR_IS: // integer string @@ -2189,7 +2261,7 @@ void FromDcmtkBridge::InitializeCodecs() { #if ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1 - LOG(INFO) << "Registering JPEG Lossless codecs in DCMTK"; + CLOG(INFO, DICOM) << "Registering JPEG Lossless codecs in DCMTK"; DJLSDecoderRegistration::registerCodecs(); # if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 DJLSEncoderRegistration::registerCodecs(); @@ -2197,14 +2269,14 @@ #endif #if ORTHANC_ENABLE_DCMTK_JPEG == 1 - LOG(INFO) << "Registering JPEG codecs in DCMTK"; + CLOG(INFO, DICOM) << "Registering JPEG codecs in DCMTK"; DJDecoderRegistration::registerCodecs(); # if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 DJEncoderRegistration::registerCodecs(); # endif #endif - LOG(INFO) << "Registering RLE codecs in DCMTK"; + CLOG(INFO, DICOM) << "Registering RLE codecs in DCMTK"; DcmRLEDecoderRegistration::registerCodecs(); #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 DcmRLEEncoderRegistration::registerCodecs(); diff -r 651f069c7f77 -r dd6274412ff4 OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h --- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h Thu Jun 17 14:00:34 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h Thu Jun 17 15:47:21 2021 +0200 @@ -103,8 +103,22 @@ Encoding target); public: + /** + * Initialize DCMTK to use the default DICOM dictionaries (either + * embedded into the binaries for official releases, or using the + * environment variable "DCM_DICT_ENVIRONMENT_VARIABLE", or using + * the system-wide path to the DCMTK library for developers) + **/ static void InitializeDictionary(bool loadPrivateDictionary); + /** + * Replace the default DICOM dictionaries by the manually-provided + * external dictionaries. This is needed to use DICONDE for + * instance. Pay attention to the fact that the current dictionary + * will be reinitialized (all its tags are cleared). + **/ + static void LoadExternalDictionaries(const std::vector& dictionaries); + static void RegisterDictionaryTag(const DicomTag& tag, ValueRepresentation vr, const std::string& name, diff -r 651f069c7f77 -r dd6274412ff4 OrthancFramework/Sources/OrthancFramework.cpp --- a/OrthancFramework/Sources/OrthancFramework.cpp Thu Jun 17 14:00:34 2021 +0200 +++ b/OrthancFramework/Sources/OrthancFramework.cpp Thu Jun 17 15:47:21 2021 +0200 @@ -79,7 +79,7 @@ #endif #if ORTHANC_ENABLE_DCMTK == 1 - FromDcmtkBridge::InitializeDictionary(true); + FromDcmtkBridge::InitializeDictionary(loadPrivateDictionary); FromDcmtkBridge::InitializeCodecs(); #endif diff -r 651f069c7f77 -r dd6274412ff4 OrthancFramework/UnitTestsSources/DicomMapTests.cpp --- a/OrthancFramework/UnitTestsSources/DicomMapTests.cpp Thu Jun 17 14:00:34 2021 +0200 +++ b/OrthancFramework/UnitTestsSources/DicomMapTests.cpp Thu Jun 17 15:47:21 2021 +0200 @@ -644,7 +644,7 @@ ASSERT_EQ(1u, b.GetSize()); ASSERT_EQ("TEST", b.GetStringValue(*it, "", false)); - std::string main = it->GetMainTagsName(); + std::string main = FromDcmtkBridge::GetTagName(*it, ""); if (!main.empty()) { ASSERT_EQ(main, name); diff -r 651f069c7f77 -r dd6274412ff4 OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp --- a/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Thu Jun 17 14:00:34 2021 +0200 +++ b/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Thu Jun 17 15:47:21 2021 +0200 @@ -700,7 +700,7 @@ { FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7057, 0x1000), ValueRepresentation_OtherByte, "MyPrivateTag2", 1, 1, "ORTHANC"); FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7059, 0x1000), ValueRepresentation_OtherByte, "MyPrivateTag3", 1, 1, ""); - FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PersonName, "Declared public tag2", 1, 1, ""); + FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1002), ValueRepresentation_PersonName, "Declared public tag2", 1, 1, ""); Json::Value v; const std::string sopClassUid = "1.2.840.10008.5.1.4.1.1.1"; // CR Image Storage: @@ -713,7 +713,7 @@ v["SOPClassUID"] = sopClassUid; v["SpecificCharacterSet"] = "ISO_IR 148"; // This is latin-5 v["PatientName"] = "Sébastien"; - v["7050-1000"] = "Some public tag"; // Even group => public tag + v["7050-1002"] = "Some public tag"; // Even group => public tag v["7052-1000"] = "Some unknown tag"; // Even group => public, unknown tag v["7057-1000"] = "Some private tag"; // Odd group => private tag v["7059-1000"] = "Some private tag2"; // Odd group => private tag, with an odd length to test padding @@ -784,7 +784,7 @@ ASSERT_EQ(2u, vv["0040,0100"].size()); ASSERT_EQ("MR", vv["0040,0100"][0]["0008,0060"].asString()); ASSERT_EQ("CT", vv["0040,0100"][1]["0008,0060"].asString()); - ASSERT_EQ("Some public tag", vv["7050,1000"].asString()); + ASSERT_EQ("Some public tag", vv["7050,1002"].asString()); ASSERT_EQ("Some unknown tag", vv["7052,1000"].asString()); ASSERT_EQ("Some private tag", vv["7057,1000"].asString()); ASSERT_EQ("Some private tag2", vv["7059,1000"].asString()); diff -r 651f069c7f77 -r dd6274412ff4 OrthancServer/Resources/Configuration.json --- a/OrthancServer/Resources/Configuration.json Thu Jun 17 14:00:34 2021 +0200 +++ b/OrthancServer/Resources/Configuration.json Thu Jun 17 15:47:21 2021 +0200 @@ -625,6 +625,18 @@ // "2001,5f" : [ "SQ", "StackSequence", 1, 1, "Philips Imaging DD 001" ] }, + // Load a set of external DICOM dictionaries in order to replace the + // default dictionaries. This option must contain a set of files in + // the DCMTK format. The order of the dictionaries *is* + // important. This option can be used to turn Orhanc into a DICONDE + // server. (new in Orthanc 1.9.4) + /** + "ExternalDictionaries" : [ + "/usr/share/libdcmtk12/dicom.dic", + "/usr/share/libdcmtk12/diconde.dic" + ] + **/ + // Whether to run DICOM C-MOVE operations synchronously. If set to // "false" (asynchronous mode), each incoming C-MOVE request results // in the creation of a new background job. Up to Orthanc 1.3.2, the diff -r 651f069c7f77 -r dd6274412ff4 OrthancServer/Sources/OrthancInitialization.cpp --- a/OrthancServer/Sources/OrthancInitialization.cpp Thu Jun 17 14:00:34 2021 +0200 +++ b/OrthancServer/Sources/OrthancInitialization.cpp Thu Jun 17 15:47:21 2021 +0200 @@ -53,6 +53,7 @@ #include "../../OrthancFramework/Sources/HttpClient.h" #include "../../OrthancFramework/Sources/Logging.h" #include "../../OrthancFramework/Sources/OrthancException.h" +#include "../../OrthancFramework/Sources/SerializationToolbox.h" #include "Database/SQLiteDatabaseWrapper.h" #include "OrthancConfiguration.h" @@ -62,14 +63,19 @@ #include // For DCM_dcmnetLogger +static const char* const STORAGE_DIRECTORY = "StorageDirectory"; +static const char* const ORTHANC_STORAGE = "OrthancStorage"; + namespace Orthanc { static void RegisterUserMetadata(const Json::Value& config) { - if (config.isMember("UserMetadata")) + static const char* const USER_METADATA = "UserMetadata"; + + if (config.isMember(USER_METADATA)) { - const Json::Value& parameter = config["UserMetadata"]; + const Json::Value& parameter = config[USER_METADATA]; Json::Value::Members members = parameter.getMemberNames(); for (size_t i = 0; i < members.size(); i++) @@ -103,9 +109,11 @@ static void RegisterUserContentType(const Json::Value& config) { - if (config.isMember("UserContentType")) + static const char* const USER_CONTENT_TYPE = "UserContentType"; + + if (config.isMember(USER_CONTENT_TYPE)) { - const Json::Value& parameter = config["UserContentType"]; + const Json::Value& parameter = config[USER_CONTENT_TYPE]; Json::Value::Members members = parameter.getMemberNames(); for (size_t i = 0; i < members.size(); i++) @@ -150,20 +158,36 @@ } + static void LoadExternalDictionaries(const Json::Value& configuration) + { + static const char* const EXTERNAL_DICTIONARIES = "ExternalDictionaries"; + + if (configuration.type() == Json::objectValue && + configuration.isMember(EXTERNAL_DICTIONARIES)) + { + std::vector dictionaries; + SerializationToolbox::ReadArrayOfStrings(dictionaries, configuration, EXTERNAL_DICTIONARIES); + FromDcmtkBridge::LoadExternalDictionaries(dictionaries); + } + } + + static void LoadCustomDictionary(const Json::Value& configuration) { + static const char* const DICTIONARY = "Dictionary"; + if (configuration.type() != Json::objectValue || - !configuration.isMember("Dictionary") || - configuration["Dictionary"].type() != Json::objectValue) + !configuration.isMember(DICTIONARY) || + configuration[DICTIONARY].type() != Json::objectValue) { return; } - Json::Value::Members tags(configuration["Dictionary"].getMemberNames()); + Json::Value::Members tags(configuration[DICTIONARY].getMemberNames()); for (Json::Value::ArrayIndex i = 0; i < tags.size(); i++) { - const Json::Value& content = configuration["Dictionary"][tags[i]]; + const Json::Value& content = configuration[DICTIONARY][tags[i]]; if (content.type() != Json::arrayValue || content.size() < 2 || content.size() > 5 || @@ -190,9 +214,13 @@ static void ConfigurePkcs11(const Json::Value& config) { + static const char* const MODULE = "Module"; + static const char* const VERBOSE = "Verbose"; + static const char* const PIN = "Pin"; + if (config.type() != Json::objectValue || - !config.isMember("Module") || - config["Module"].type() != Json::stringValue) + !config.isMember(MODULE) || + config[MODULE].type() != Json::stringValue) { throw OrthancException(ErrorCode_BadFileFormat, "No path to the PKCS#11 module (DLL or .so) is provided " @@ -200,11 +228,11 @@ } std::string pin; - if (config.isMember("Pin")) + if (config.isMember(PIN)) { - if (config["Pin"].type() == Json::stringValue) + if (config[PIN].type() == Json::stringValue) { - pin = config["Pin"].asString(); + pin = config[PIN].asString(); } else { @@ -214,11 +242,11 @@ } bool verbose = false; - if (config.isMember("Verbose")) + if (config.isMember(VERBOSE)) { - if (config["Verbose"].type() == Json::booleanValue) + if (config[VERBOSE].type() == Json::booleanValue) { - verbose = config["Verbose"].asBool(); + verbose = config[VERBOSE].asBool(); } else { @@ -227,17 +255,18 @@ } } - HttpClient::InitializePkcs11(config["Module"].asString(), pin, verbose); + HttpClient::InitializePkcs11(config[MODULE].asString(), pin, verbose); } void OrthancInitialize(const char* configurationFile) { - static const char* LOCALE = "Locale"; - static const char* PKCS11 = "Pkcs11"; - static const char* DEFAULT_ENCODING = "DefaultEncoding"; - static const char* MALLOC_ARENA_MAX = "MallocArenaMax"; + static const char* const LOCALE = "Locale"; + static const char* const PKCS11 = "Pkcs11"; + static const char* const DEFAULT_ENCODING = "DefaultEncoding"; + static const char* const MALLOC_ARENA_MAX = "MallocArenaMax"; + static const char* const LOAD_PRIVATE_DICTIONARY = "LoadPrivateDictionary"; OrthancConfiguration::WriterLock lock; @@ -254,7 +283,7 @@ locale = lock.GetConfiguration().GetStringParameter(LOCALE, ""); } - bool loadPrivate = lock.GetConfiguration().GetBooleanParameter("LoadPrivateDictionary", true); + bool loadPrivate = lock.GetConfiguration().GetBooleanParameter(LOAD_PRIVATE_DICTIONARY, true); Orthanc::InitializeFramework(locale, loadPrivate); } @@ -278,6 +307,7 @@ RegisterUserMetadata(lock.GetJson()); RegisterUserContentType(lock.GetJson()); + LoadExternalDictionaries(lock.GetJson()); // New in Orthanc 1.9.4 LoadCustomDictionary(lock.GetJson()); lock.GetConfiguration().RegisterFont(ServerResources::FONT_UBUNTU_MONO_BOLD_16); @@ -319,7 +349,7 @@ OrthancConfiguration::ReaderLock lock; std::string storageDirectoryStr = - lock.GetConfiguration().GetStringParameter("StorageDirectory", "OrthancStorage"); + lock.GetConfiguration().GetStringParameter(STORAGE_DIRECTORY, ORTHANC_STORAGE); // Open the database boost::filesystem::path indexDirectory = lock.GetConfiguration().InterpretStringParameterAsPath( @@ -413,10 +443,13 @@ static IStorageArea* CreateFilesystemStorage() { + static const char* const SYNC_STORAGE_AREA = "SyncStorageArea"; + static const char* const STORE_DICOM = "StoreDicom"; + OrthancConfiguration::ReaderLock lock; std::string storageDirectoryStr = - lock.GetConfiguration().GetStringParameter("StorageDirectory", "OrthancStorage"); + lock.GetConfiguration().GetStringParameter(STORAGE_DIRECTORY, ORTHANC_STORAGE); boost::filesystem::path storageDirectory = lock.GetConfiguration().InterpretStringParameterAsPath(storageDirectoryStr); @@ -424,9 +457,9 @@ LOG(WARNING) << "Storage directory: " << storageDirectory; // New in Orthanc 1.7.4 - bool fsyncOnWrite = lock.GetConfiguration().GetBooleanParameter("SyncStorageArea", true); + bool fsyncOnWrite = lock.GetConfiguration().GetBooleanParameter(SYNC_STORAGE_AREA, true); - if (lock.GetConfiguration().GetBooleanParameter("StoreDicom", true)) + if (lock.GetConfiguration().GetBooleanParameter(STORE_DICOM, true)) { return new FilesystemStorage(storageDirectory.string(), fsyncOnWrite); }