# HG changeset patch # User Sebastien Jodogne # Date 1625494330 -7200 # Node ID b51c08bd5c381f87337004222794b93f137e64f6 # Parent 1db3b79d97bdc5a2ee74425e17b43c970a7e8d9f added ITagVisitor::Action_Remove diff -r 1db3b79d97bd -r b51c08bd5c38 OrthancFramework/Sources/DicomParsing/DicomModification.cpp --- a/OrthancFramework/Sources/DicomParsing/DicomModification.cpp Mon Jun 28 14:25:37 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/DicomModification.cpp Mon Jul 05 16:12:10 2021 +0200 @@ -84,49 +84,55 @@ { } - virtual void VisitNotSupported(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr) + virtual Action VisitNotSupported(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr) ORTHANC_OVERRIDE { + return Action_None; } - virtual void VisitEmptySequence(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag) + virtual Action VisitEmptySequence(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag) ORTHANC_OVERRIDE { + return Action_None; } - virtual void VisitBinary(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr, - const void* data, - size_t size) - { - } - - virtual void VisitIntegers(const std::vector& parentTags, + virtual Action VisitBinary(const std::vector& parentTags, const std::vector& parentIndexes, const DicomTag& tag, ValueRepresentation vr, - const std::vector& values) + const void* data, + size_t size) ORTHANC_OVERRIDE { + return Action_None; + } + + virtual Action VisitIntegers(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const std::vector& values) ORTHANC_OVERRIDE + { + return Action_None; } - virtual void VisitDoubles(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr, - const std::vector& value) + virtual Action VisitDoubles(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const std::vector& value) ORTHANC_OVERRIDE { + return Action_None; } - virtual void VisitAttributes(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - const std::vector& value) + virtual Action VisitAttributes(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + const std::vector& value) ORTHANC_OVERRIDE { + return Action_None; } virtual Action VisitString(std::string& newValue, @@ -134,7 +140,7 @@ const std::vector& parentIndexes, const DicomTag& tag, ValueRepresentation vr, - const std::string& value) + const std::string& value) ORTHANC_OVERRIDE { /** * Note that all the tags in "uids_" have the VR UI (unique diff -r 1db3b79d97bd -r b51c08bd5c38 OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp --- a/OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp Mon Jun 28 14:25:37 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp Mon Jul 05 16:12:10 2021 +0200 @@ -371,32 +371,38 @@ #endif - void DicomWebJsonVisitor::VisitNotSupported(const std::vector &parentTags, - const std::vector &parentIndexes, - const DicomTag &tag, - ValueRepresentation vr) + ITagVisitor::Action + DicomWebJsonVisitor::VisitNotSupported(const std::vector &parentTags, + const std::vector &parentIndexes, + const DicomTag &tag, + ValueRepresentation vr) { + return Action_None; } - void DicomWebJsonVisitor::VisitEmptySequence(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag) + ITagVisitor::Action + DicomWebJsonVisitor::VisitEmptySequence(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag) { if (tag.GetElement() != 0x0000) { Json::Value& node = CreateNode(parentTags, parentIndexes, tag); node[KEY_VR] = EnumerationToString(ValueRepresentation_Sequence); } + + return Action_None; } - void DicomWebJsonVisitor::VisitBinary(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr, - const void* data, - size_t size) + ITagVisitor::Action + DicomWebJsonVisitor::VisitBinary(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const void* data, + size_t size) { assert(vr == ValueRepresentation_OtherByte || vr == ValueRepresentation_OtherDouble || @@ -456,14 +462,17 @@ } } } + + return Action_None; } - void DicomWebJsonVisitor::VisitIntegers(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr, - const std::vector& values) + ITagVisitor::Action + DicomWebJsonVisitor::VisitIntegers(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const std::vector& values) { if (tag.GetElement() != 0x0000 && vr != ValueRepresentation_NotSupported) @@ -482,13 +491,16 @@ node[KEY_VALUE] = content; } } + + return Action_None; } - void DicomWebJsonVisitor::VisitDoubles(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr, - const std::vector& values) + ITagVisitor::Action + DicomWebJsonVisitor::VisitDoubles(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const std::vector& values) { if (tag.GetElement() != 0x0000 && vr != ValueRepresentation_NotSupported) @@ -507,13 +519,16 @@ node[KEY_VALUE] = content; } } + + return Action_None; } - void DicomWebJsonVisitor::VisitAttributes(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - const std::vector& values) + ITagVisitor::Action + DicomWebJsonVisitor::VisitAttributes(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + const std::vector& values) { if (tag.GetElement() != 0x0000) { @@ -531,6 +546,8 @@ node[KEY_VALUE] = content; } } + + return Action_None; } diff -r 1db3b79d97bd -r b51c08bd5c38 OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.h --- a/OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.h Mon Jun 28 14:25:37 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.h Mon Jul 05 16:12:10 2021 +0200 @@ -85,43 +85,43 @@ void FormatXml(std::string& target) const; #endif - virtual void VisitNotSupported(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr) + virtual Action VisitNotSupported(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr) ORTHANC_OVERRIDE; - virtual void VisitEmptySequence(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag) + virtual Action VisitEmptySequence(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag) ORTHANC_OVERRIDE; - virtual void VisitBinary(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr, - const void* data, - size_t size) - ORTHANC_OVERRIDE; - - virtual void VisitIntegers(const std::vector& parentTags, + virtual Action VisitBinary(const std::vector& parentTags, const std::vector& parentIndexes, const DicomTag& tag, ValueRepresentation vr, - const std::vector& values) + const void* data, + size_t size) + ORTHANC_OVERRIDE; + + virtual Action VisitIntegers(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const std::vector& values) ORTHANC_OVERRIDE; - virtual void VisitDoubles(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr, - const std::vector& values) + virtual Action VisitDoubles(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const std::vector& values) ORTHANC_OVERRIDE; - virtual void VisitAttributes(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - const std::vector& values) + virtual Action VisitAttributes(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + const std::vector& values) ORTHANC_OVERRIDE; virtual Action VisitString(std::string& newValue, diff -r 1db3b79d97bd -r b51c08bd5c38 OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp --- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Mon Jun 28 14:25:37 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Mon Jul 05 16:12:10 2021 +0200 @@ -2340,7 +2340,7 @@ // Forward declaration - static void ApplyVisitorToElement(DcmElement& element, + static bool ApplyVisitorToElement(DcmElement& element, ITagVisitor& visitor, const std::vector& parentTags, const std::vector& parentIndexes, @@ -2356,6 +2356,8 @@ { assert(parentTags.size() == parentIndexes.size()); + std::set toRemove; + for (unsigned long i = 0; i < dataset.card(); i++) { DcmElement* element = dataset.getElement(i); @@ -2365,13 +2367,25 @@ } else { - ApplyVisitorToElement(*element, visitor, parentTags, parentIndexes, encoding, hasCodeExtensions); + if (!ApplyVisitorToElement(*element, visitor, parentTags, parentIndexes, encoding, hasCodeExtensions)) + { + toRemove.insert(element->getTag()); + } } } + + // Remove all the tags that were planned for removal (cf. ITagVisitor::Action_Remove) + for (std::set::const_iterator + it = toRemove.begin(); it != toRemove.end(); ++it) + { + std::unique_ptr tmp(dataset.remove(*it)); + } } - static void ApplyVisitorToLeaf(DcmElement& element, + // Returns "true" iff the element must be kept. If "false" is + // returned, the element will be removed. + static bool ApplyVisitorToLeaf(DcmElement& element, ITagVisitor& visitor, const std::vector& parentTags, const std::vector& parentIndexes, @@ -2415,11 +2429,13 @@ Uint16* data16 = NULL; Uint8* data = NULL; + ITagVisitor::Action action; + if ((element.getTag() == DCM_PixelData || // (*) New in Orthanc 1.9.1 evr == EVR_OW) && element.getUint16Array(data16) == EC_Normal) { - visitor.VisitBinary(parentTags, parentIndexes, tag, vr, data16, element.getLength()); + action = visitor.VisitBinary(parentTags, parentIndexes, tag, vr, data16, element.getLength()); } else if (evr != EVR_OW && element.getUint8Array(data) == EC_Normal) @@ -2432,14 +2448,27 @@ * reimplemented in derived class "DcmPixelData"). However, * "getUint16Array()" works correctly, hence (*). **/ - visitor.VisitBinary(parentTags, parentIndexes, tag, vr, data, element.getLength()); + action = visitor.VisitBinary(parentTags, parentIndexes, tag, vr, data, element.getLength()); } else { - visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); + action = visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); } - return; // We're done + switch (action) + { + case ITagVisitor::Action_None: + return true; // We're done + + case ITagVisitor::Action_Remove: + return false; + + case ITagVisitor::Action_Replace: + throw OrthancException(ErrorCode_NotImplemented, "Iterator cannot replace binary data"); + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } } @@ -2473,7 +2502,10 @@ switch (action) { case ITagVisitor::Action_None: - break; + return true; + + case ITagVisitor::Action_Remove: + return false; case ITagVisitor::Action_Replace: { @@ -2481,20 +2513,20 @@ if (element.putString(s.c_str()) != EC_Normal) { throw OrthancException(ErrorCode_InternalError, - "Cannot replace value of tag: " + tag.Format()); + "Iterator cannot replace value of tag: " + tag.Format()); } - break; + return true; } default: throw OrthancException(ErrorCode_InternalError); } - - return; // We're done } + ITagVisitor::Action action; + try { // http://support.dcmtk.org/docs/dcvr_8h-source.html @@ -2522,7 +2554,7 @@ case EVR_UI: // unique identifier { Uint8* data = NULL; - + if (element.getUint8Array(data) == EC_Normal) { const Uint32 length = element.getLength(); @@ -2536,30 +2568,30 @@ if (l == length) { // Not a null-terminated plain string - visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); + action = visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); } else { std::string ignored; std::string s(reinterpret_cast(data), l); - ITagVisitor::Action action = visitor.VisitString - (ignored, parentTags, parentIndexes, tag, vr, - Toolbox::ConvertToUtf8(s, encoding, hasCodeExtensions)); - - if (action != ITagVisitor::Action_None) - { - LOG(WARNING) << "Cannot replace this string tag: " - << FromDcmtkBridge::GetTagName(element) - << " (" << tag.Format() << ")"; - } + action = visitor.VisitString(ignored, parentTags, parentIndexes, tag, vr, + Toolbox::ConvertToUtf8(s, encoding, hasCodeExtensions)); } } else { - visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); + action = visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); } - return; + if (action == ITagVisitor::Action_Replace) + { + LOG(WARNING) << "Iterator cannot replace this string tag: " + << FromDcmtkBridge::GetTagName(element) + << " (" << tag.Format() << ")"; + return true; + } + + break; } /** @@ -2582,7 +2614,7 @@ } } - visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); + action = visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); break; } @@ -2602,7 +2634,7 @@ } } - visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); + action = visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); break; } @@ -2625,7 +2657,7 @@ } } - visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); + action = visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); break; } @@ -2645,7 +2677,7 @@ } } - visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); + action = visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); break; } @@ -2666,7 +2698,7 @@ } } - visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values); + action = visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values); break; } @@ -2689,7 +2721,7 @@ } } - visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values); + action = visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values); break; } @@ -2716,7 +2748,7 @@ } assert(vr == ValueRepresentation_AttributeTag); - visitor.VisitAttributes(parentTags, parentIndexes, tag, values); + action = visitor.VisitAttributes(parentTags, parentIndexes, tag, values); break; } @@ -2728,7 +2760,7 @@ case EVR_SQ: // sequence of items { - return; + return true; } @@ -2751,8 +2783,8 @@ case EVR_PixelData: // used internally for uncompressed pixeld data case EVR_OverlayData: // used internally for overlay data { - visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); - return; + action = visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); + break; } @@ -2761,21 +2793,38 @@ **/ default: - return; + return true; + } + + switch (action) + { + case ITagVisitor::Action_None: + return true; // We're done + + case ITagVisitor::Action_Remove: + return false; + + case ITagVisitor::Action_Replace: + throw OrthancException(ErrorCode_NotImplemented, "Iterator cannot replace non-string-like data"); + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); } } catch (boost::bad_lexical_cast&) { - return; + return true; } catch (std::bad_cast&) { - return; + return true; } } - static void ApplyVisitorToElement(DcmElement& element, + // Returns "true" iff the element must be kept. If "false" is + // returned, the element will be removed. + static bool ApplyVisitorToElement(DcmElement& element, ITagVisitor& visitor, const std::vector& parentTags, const std::vector& parentIndexes, @@ -2788,7 +2837,7 @@ if (element.isLeaf()) { - ApplyVisitorToLeaf(element, visitor, parentTags, parentIndexes, tag, encoding, hasCodeExtensions); + return ApplyVisitorToLeaf(element, visitor, parentTags, parentIndexes, tag, encoding, hasCodeExtensions); } else { @@ -2799,7 +2848,22 @@ if (sequence.card() == 0) { - visitor.VisitEmptySequence(parentTags, parentIndexes, tag); + ITagVisitor::Action action = visitor.VisitEmptySequence(parentTags, parentIndexes, tag); + + switch (action) + { + case ITagVisitor::Action_None: + return true; + + case ITagVisitor::Action_Remove: + return false; + + case ITagVisitor::Action_Replace: + throw OrthancException(ErrorCode_NotImplemented, "Iterator cannot replace sequences"); + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } } else { @@ -2814,6 +2878,8 @@ DcmItem* child = sequence.getItem(i); ApplyVisitorToDataset(*child, visitor, tags, indexes, encoding, hasCodeExtensions); } + + return true; // Keep } } } diff -r 1db3b79d97bd -r b51c08bd5c38 OrthancFramework/Sources/DicomParsing/ITagVisitor.h --- a/OrthancFramework/Sources/DicomParsing/ITagVisitor.h Mon Jun 28 14:25:37 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/ITagVisitor.h Mon Jul 05 16:12:10 2021 +0200 @@ -35,6 +35,7 @@ enum Action { Action_Replace, + Action_Remove, // New in Orthanc 1.9.5 Action_None }; @@ -42,46 +43,47 @@ { } - // Visiting a DICOM element that is internal to DCMTK - virtual void VisitNotSupported(const std::vector& parentTags, + // Visiting a DICOM element that is internal to DCMTK. Can return + // "Remove" or "None". + virtual Action VisitNotSupported(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr) = 0; + + // SQ - can return "Remove" or "None" + virtual Action VisitEmptySequence(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag) = 0; + + // SL, SS, UL, US - can return "Remove" or "None" + virtual Action VisitIntegers(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const std::vector& values) = 0; + + // FL, FD, OD, OF - can return "Remove" or "None" + virtual Action VisitDoubles(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const std::vector& values) = 0; + + // AT - can return "Remove" or "None" + virtual Action VisitAttributes(const std::vector& parentTags, const std::vector& parentIndexes, const DicomTag& tag, - ValueRepresentation vr) = 0; + const std::vector& values) = 0; - // SQ - virtual void VisitEmptySequence(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag) = 0; - - // SL, SS, UL, US - virtual void VisitIntegers(const std::vector& parentTags, + // OB, OL, OW, UN - can return "Remove" or "None" + virtual Action VisitBinary(const std::vector& parentTags, const std::vector& parentIndexes, const DicomTag& tag, ValueRepresentation vr, - const std::vector& values) = 0; - - // FL, FD, OD, OF - virtual void VisitDoubles(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr, - const std::vector& values) = 0; + const void* data, + size_t size) = 0; - // AT - virtual void VisitAttributes(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - const std::vector& values) = 0; - - // OB, OL, OW, UN - virtual void VisitBinary(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr, - const void* data, - size_t size) = 0; - - // Visiting an UTF-8 string + // Visiting an UTF-8 string - can return "Replace", "Remove" or "None" virtual Action VisitString(std::string& newValue, const std::vector& parentTags, const std::vector& parentIndexes, diff -r 1db3b79d97bd -r b51c08bd5c38 OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp --- a/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Mon Jun 28 14:25:37 2021 +0200 +++ b/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Mon Jul 05 16:12:10 2021 +0200 @@ -61,6 +61,9 @@ #include #include #include +#include +#include +#include #include #include @@ -2710,6 +2713,240 @@ } +TEST(FromDcmtkBridge, VisitorRemoveTag) +{ + class V : public ITagVisitor + { + private: + uint32_t seen_; + + public: + V() : seen_(0) + { + } + + unsigned int GetSeen() const + { + return seen_; + } + + virtual Action VisitNotSupported(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr) ORTHANC_OVERRIDE + { + seen_ |= (1 << 0); + + if (parentTags.size() == 0u && + parentIndexes.size() == 0u && + DcmTagKey(tag.GetGroup(), tag.GetElement()) == DCM_PixelData) + { + return Action_Remove; + } + else + { + throw OrthancException(ErrorCode_InternalError); + } + } + + virtual Action VisitEmptySequence(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag) ORTHANC_OVERRIDE + { + seen_ |= (1 << 1); + + if (parentTags.size() == 1u && + parentIndexes.size() == 1u && + parentTags[0] == DICOM_TAG_REFERENCED_IMAGE_SEQUENCE && + parentIndexes[0] == 0u && + DcmTagKey(tag.GetGroup(), tag.GetElement()) == DCM_ReferencedPatientSequence) + { + return Action_Remove; + } + else + { + throw OrthancException(ErrorCode_InternalError); + } + } + + virtual Action VisitIntegers(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const std::vector& values) ORTHANC_OVERRIDE + { + seen_ |= (1 << 2); + + if (parentTags.size() == 0u && + parentIndexes.size() == 0u && + DcmTagKey(tag.GetGroup(), tag.GetElement()) == DCM_TagAngleSecondAxis && + values.size() == 2 && + values[0] == 12 && + values[1] == 13) + { + return Action_Remove; + } + else + { + throw OrthancException(ErrorCode_InternalError); + } + } + + virtual Action VisitDoubles(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const std::vector& values) ORTHANC_OVERRIDE + { + seen_ |= (1 << 3); + + if (parentTags.size() == 1u && + parentIndexes.size() == 1u && + parentTags[0] == DICOM_TAG_REFERENCED_IMAGE_SEQUENCE && + parentIndexes[0] == 0u && + DcmTagKey(tag.GetGroup(), tag.GetElement()) == DCM_ExaminedBodyThickness && + values.size() == 3 && + std::abs(values[0] - 42.0f) <= 0.001f && + std::abs(values[1] - 43.0f) <= 0.001f && + std::abs(values[2] - 47.0f) <= 0.001f) + { + return Action_Remove; + } + else + { + throw OrthancException(ErrorCode_InternalError); + } + } + + virtual Action VisitAttributes(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + const std::vector& values) ORTHANC_OVERRIDE + { + seen_ |= (1 << 4); + + if (parentTags.size() == 1u && + parentIndexes.size() == 1u && + parentTags[0] == DICOM_TAG_REFERENCED_IMAGE_SEQUENCE && + parentIndexes[0] == 0u && + DcmTagKey(tag.GetGroup(), tag.GetElement()) == DCM_DimensionIndexPointer && + values.size() == 2 && + values[0] == DICOM_TAG_STUDY_DATE && + values[1] == DICOM_TAG_STUDY_TIME) + { + return Action_Remove; + } + else + { + throw OrthancException(ErrorCode_InternalError); + } + } + + virtual Action VisitBinary(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const void* data, + size_t size) ORTHANC_OVERRIDE + { + seen_ |= (1 << 5); + + if (parentTags.size() == 1u && + parentIndexes.size() == 1u && + parentTags[0] == DICOM_TAG_REFERENCED_IMAGE_SEQUENCE && + parentIndexes[0] == 0u && + tag.GetGroup() == 0x0011 && + tag.GetElement() == 0x1311 && + size == 4u && + memcmp(data, "abcd", 4) == 0) + { + return Action_Remove; + } + else + { + throw OrthancException(ErrorCode_InternalError); + } + } + + virtual Action VisitString(std::string& newValue, + const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const std::string& value) ORTHANC_OVERRIDE + { + seen_ |= (1 << 6); + return Action_Remove; + } + }; + + + std::unique_ptr dicom; + + { + Json::Value v = Json::objectValue; + v["PatientName"] = "Hello"; + v["ReferencedSOPClassUID"] = "1.2.840.10008.5.1.4.1.1.4"; + v["ReferencedImageSequence"][0]["ReferencedSOPClassUID"] = "1.2.840.10008.5.1.4.1.1.4"; + v["ReferencedImageSequence"][0]["ReferencedSOPInstanceUID"] = "1.2.840.113619.2.176.2025.1499492.7040.1171286241.719"; + v["ReferencedImageSequence"][0]["ReferencedPatientSequence"] = Json::arrayValue; // Empty sequence + v["ReferencedImageSequence"][0]["0011,1311"] = "abcd"; // Binary + + dicom.reset(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "PrivateCreator")); + + { + // Test value multiplicity (cannot be done using "ParsedDicomFile::CreateFromJson()") + const int16_t a[] = { 12, 13 }; + std::unique_ptr s(new DcmSignedShort(DCM_TagAngleSecondAxis)); // VisitIntegers() + ASSERT_TRUE(s->putSint16Array(a, 2).good()); + dicom->GetDcmtkObject().getDataset()->insert(s.release()); + } + + { + const float a[] = { 42, 43, 47 }; + std::unique_ptr s(new DcmFloatingPointSingle(DCM_ExaminedBodyThickness)); // VisitDoubles() + ASSERT_TRUE(s->putFloat32Array(a, 3).good()); + DcmItem *item = NULL; + ASSERT_TRUE(dicom->GetDcmtkObject().getDataset()->findAndGetSequenceItem(DCM_ReferencedImageSequence, item, 0).good()); + item->insert(s.release()); + } + + { + const uint16_t a[] = { 0x0008, 0x0020, 0x0008, 0x0030 }; + std::unique_ptr s(new DcmAttributeTag(DCM_DimensionIndexPointer)); // VisitAttributes() + ASSERT_TRUE(s->putUint16Array(a, 2).good()); + DcmItem *item = NULL; + ASSERT_TRUE(dicom->GetDcmtkObject().getDataset()->findAndGetSequenceItem(DCM_ReferencedImageSequence, item, 0).good()); + item->insert(s.release()); + } + + ASSERT_TRUE(dicom->GetDcmtkObject().getDataset()->insert(new DcmByteString(DCM_PixelData)).good()); // VisitNotSupported() + } + + { + V visitor; + dicom->Apply(visitor); + ASSERT_EQ(127u, visitor.GetSeen()); // Make sure all the methods have been applied + } + + { + Json::Value b; + dicom->DatasetToJson(b, DicomToJsonFormat_Short, DicomToJsonFlags_Default, 0); + ASSERT_EQ(Json::objectValue, b.type()); + + Json::Value::Members members = b.getMemberNames(); + ASSERT_EQ(1u, members.size()); + ASSERT_EQ("0008,1140", members[0]); + + // Check that "b["0008,1140"]" is a sequence with one single empty object + ASSERT_EQ(Json::arrayValue, b["0008,1140"].type()); + ASSERT_EQ(1u, b["0008,1140"].size()); + ASSERT_EQ(Json::objectValue, b["0008,1140"][0].type()); + ASSERT_EQ(0u, b["0008,1140"][0].size()); + } +} + + #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1