comparison OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp @ 4527:31f940334496

Handle public tags with "UN" value representation and containing a string (cf. DICOM CP 246)
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 24 Feb 2021 12:39:35 +0100
parents 671ee7c1fd46
children 93a51d228d80
comparison
equal deleted inserted replaced
4526:59b667dd58a8 4527:31f940334496
552 { 552 {
553 return DicomTag(element.getGTag(), element.getETag()); 553 return DicomTag(element.getGTag(), element.getETag());
554 } 554 }
555 555
556 556
557 static DicomValue* CreateValueFromUtf8String(const DicomTag& tag,
558 const std::string& utf8,
559 unsigned int maxStringLength,
560 const std::set<DicomTag>& ignoreTagLength)
561 {
562 if (maxStringLength != 0 &&
563 utf8.size() > maxStringLength &&
564 ignoreTagLength.find(tag) == ignoreTagLength.end())
565 {
566 return new DicomValue; // Too long, create a NULL value
567 }
568 else
569 {
570 return new DicomValue(utf8, false);
571 }
572 }
573
574
557 DicomValue* FromDcmtkBridge::ConvertLeafElement(DcmElement& element, 575 DicomValue* FromDcmtkBridge::ConvertLeafElement(DcmElement& element,
558 DicomToJsonFlags flags, 576 DicomToJsonFlags flags,
559 unsigned int maxStringLength, 577 unsigned int maxStringLength,
560 Encoding encoding, 578 Encoding encoding,
561 bool hasCodeExtensions, 579 bool hasCodeExtensions,
565 { 583 {
566 // This function is only applicable to leaf elements 584 // This function is only applicable to leaf elements
567 throw OrthancException(ErrorCode_BadParameterType); 585 throw OrthancException(ErrorCode_BadParameterType);
568 } 586 }
569 587
570 char *c = NULL; 588 {
571 if (element.isaString() && 589 char *c = NULL;
572 element.getString(c).good()) 590 if (element.isaString() &&
573 { 591 element.getString(c).good())
574 if (c == NULL) // This case corresponds to the empty string 592 {
575 { 593 if (c == NULL) // This case corresponds to the empty string
576 return new DicomValue("", false); 594 {
577 } 595 return new DicomValue("", false);
578 else
579 {
580 std::string s(c);
581 std::string utf8 = Toolbox::ConvertToUtf8(s, encoding, hasCodeExtensions);
582
583 if (maxStringLength != 0 &&
584 utf8.size() > maxStringLength &&
585 ignoreTagLength.find(GetTag(element)) == ignoreTagLength.end())
586 {
587 return new DicomValue; // Too long, create a NULL value
588 } 596 }
589 else 597 else
590 { 598 {
591 return new DicomValue(utf8, false); 599 const std::string s(c);
600 const std::string utf8 = Toolbox::ConvertToUtf8(s, encoding, hasCodeExtensions);
601 return CreateValueFromUtf8String(GetTag(element), utf8, maxStringLength, ignoreTagLength);
592 } 602 }
593 } 603 }
594 } 604 }
595 605
596 606
597 if (element.getVR() == EVR_UN) 607 if (element.getVR() == EVR_UN)
598 { 608 {
599 // Unknown value representation: Lookup in the dictionary. This 609 /**
600 // is notably the case for private tags registered with the 610 * Unknown value representation: Lookup in the dictionary. This
601 // "Dictionary" configuration option. 611 * is notably the case for private tags registered with the
612 * "Dictionary" configuration option, or for public tags with
613 * "EVR_UN" in the case of Little Endian Implicit transfer
614 * syntax (cf. DICOM CP 246).
615 * ftp://medical.nema.org/medical/dicom/final/cp246_ft.pdf
616 **/
602 DictionaryLocker locker; 617 DictionaryLocker locker;
603 618
604 const DcmDictEntry* entry = locker->findEntry(element.getTag().getXTag(), 619 const DcmDictEntry* entry = locker->findEntry(element.getTag().getXTag(),
605 element.getTag().getPrivateCreator()); 620 element.getTag().getPrivateCreator());
606 if (entry != NULL && 621 if (entry != NULL &&
607 entry->getVR().isaString()) 622 entry->getVR().isaString())
608 { 623 {
609 Uint8* data = NULL; 624 Uint8* data = NULL;
610 625
611 // At (*), we do not try and convert to UTF-8, as nothing says 626 if (element.getUint8Array(data) == EC_Normal)
612 // the encoding of the private tag is the same as that of the 627 {
613 // remaining of the DICOM dataset. Only go for ASCII strings. 628 Uint32 length = element.getLength();
614 629
615 if (element.getUint8Array(data) == EC_Normal && 630 if (data == NULL ||
616 Toolbox::IsAsciiString(data, element.getLength())) // (*) 631 length == 0)
617 {
618 if (data == NULL)
619 { 632 {
620 return new DicomValue("", false); // Empty string 633 return new DicomValue("", false); // Empty string
621 } 634 }
622 else if (maxStringLength != 0 && 635
623 element.getLength() > maxStringLength && 636 // Remove the trailing padding, if any
624 ignoreTagLength.find(GetTag(element)) == ignoreTagLength.end()) 637 if (length > 0 &&
638 length % 2 == 0 &&
639 data[length - 1] == '\0')
625 { 640 {
626 return new DicomValue; // Too long, create a NULL value 641 length = length - 1;
642 }
643
644 if (element.getTag().isPrivate())
645 {
646 // For private tags, we do not try and convert to UTF-8,
647 // as nothing ensures that the encoding of the private tag
648 // is the same as that of the remaining of the DICOM
649 // dataset. Only go for ASCII strings.
650 if (Toolbox::IsAsciiString(data, length))
651 {
652 const std::string s(reinterpret_cast<const char*>(data), length);
653 return CreateValueFromUtf8String(GetTag(element), s, maxStringLength, ignoreTagLength);
654 }
655 else
656 {
657 // Not a plain ASCII string: Consider it as a binary
658 // value that is handled in the switch-case below
659 }
627 } 660 }
628 else 661 else
629 { 662 {
630 std::string s(reinterpret_cast<const char*>(data), element.getLength()); 663 // For public tags, convert to UTF-8 by using the
631 return new DicomValue(s, false); 664 // "SpecificCharacterSet" tag, if present. This branch is
665 // new in Orthanc 1.9.1 (cf. DICOM CP 246).
666 const std::string s(reinterpret_cast<const char*>(data), length);
667 const std::string utf8 = Toolbox::ConvertToUtf8(s, encoding, hasCodeExtensions);
668 return CreateValueFromUtf8String(GetTag(element), utf8, maxStringLength, ignoreTagLength);
632 } 669 }
633 } 670 }
634 } 671 }
635 } 672 }
636 673