Mercurial > hg > orthanc
comparison OrthancServer/ParsedDicomFile.cpp @ 1701:4aaaecae5803 db-changes
integration mainline->db-changes
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 12 Oct 2015 14:47:58 +0200 |
parents | de1413733c97 8ca0e89798b2 |
children | a001f6226c7c |
comparison
equal
deleted
inserted
replaced
1683:21d31da73374 | 1701:4aaaecae5803 |
---|---|
135 #include <dcmtk/dcmdata/dcpxitem.h> | 135 #include <dcmtk/dcmdata/dcpxitem.h> |
136 | 136 |
137 | 137 |
138 #include <boost/math/special_functions/round.hpp> | 138 #include <boost/math/special_functions/round.hpp> |
139 #include <dcmtk/dcmdata/dcostrmb.h> | 139 #include <dcmtk/dcmdata/dcostrmb.h> |
140 #include <boost/algorithm/string/predicate.hpp> | |
140 | 141 |
141 | 142 |
142 static const char* CONTENT_TYPE_OCTET_STREAM = "application/octet-stream"; | 143 static const char* CONTENT_TYPE_OCTET_STREAM = "application/octet-stream"; |
143 | 144 |
144 | 145 |
146 namespace Orthanc | 147 namespace Orthanc |
147 { | 148 { |
148 struct ParsedDicomFile::PImpl | 149 struct ParsedDicomFile::PImpl |
149 { | 150 { |
150 std::auto_ptr<DcmFileFormat> file_; | 151 std::auto_ptr<DcmFileFormat> file_; |
151 Encoding encoding_; | |
152 }; | 152 }; |
153 | 153 |
154 | 154 |
155 // This method can only be called from the constructors! | 155 // This method can only be called from the constructors! |
156 void ParsedDicomFile::Setup(const char* buffer, size_t size) | 156 void ParsedDicomFile::Setup(const char* buffer, size_t size) |
171 | 171 |
172 throw OrthancException(ErrorCode_BadFileFormat); | 172 throw OrthancException(ErrorCode_BadFileFormat); |
173 } | 173 } |
174 pimpl_->file_->loadAllDataIntoMemory(); | 174 pimpl_->file_->loadAllDataIntoMemory(); |
175 pimpl_->file_->transferEnd(); | 175 pimpl_->file_->transferEnd(); |
176 | |
177 pimpl_->encoding_ = FromDcmtkBridge::DetectEncoding(*pimpl_->file_->getDataset()); | |
178 } | 176 } |
179 | 177 |
180 | 178 |
181 static void SendPathValueForDictionary(RestApiOutput& output, | 179 static void SendPathValueForDictionary(RestApiOutput& output, |
182 DcmItem& dicom) | 180 DcmItem& dicom) |
517 SendPathValueForLeaf(output, uri.back(), *dicom, transferSyntax); | 515 SendPathValueForLeaf(output, uri.back(), *dicom, transferSyntax); |
518 } | 516 } |
519 } | 517 } |
520 | 518 |
521 | 519 |
522 | |
523 | |
524 | |
525 static DcmElement* CreateElementForTag(const DicomTag& tag) | |
526 { | |
527 DcmTag key(tag.GetGroup(), tag.GetElement()); | |
528 | |
529 switch (key.getEVR()) | |
530 { | |
531 // http://support.dcmtk.org/docs/dcvr_8h-source.html | |
532 | |
533 /** | |
534 * TODO. | |
535 **/ | |
536 | |
537 case EVR_OB: // other byte | |
538 case EVR_OF: // other float | |
539 case EVR_OW: // other word | |
540 case EVR_AT: // attribute tag | |
541 throw OrthancException(ErrorCode_NotImplemented); | |
542 | |
543 case EVR_UN: // unknown value representation | |
544 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
545 | |
546 | |
547 /** | |
548 * String types. | |
549 * http://support.dcmtk.org/docs/classDcmByteString.html | |
550 **/ | |
551 | |
552 case EVR_AS: // age string | |
553 return new DcmAgeString(key); | |
554 | |
555 case EVR_AE: // application entity title | |
556 return new DcmApplicationEntity(key); | |
557 | |
558 case EVR_CS: // code string | |
559 return new DcmCodeString(key); | |
560 | |
561 case EVR_DA: // date string | |
562 return new DcmDate(key); | |
563 | |
564 case EVR_DT: // date time string | |
565 return new DcmDateTime(key); | |
566 | |
567 case EVR_DS: // decimal string | |
568 return new DcmDecimalString(key); | |
569 | |
570 case EVR_IS: // integer string | |
571 return new DcmIntegerString(key); | |
572 | |
573 case EVR_TM: // time string | |
574 return new DcmTime(key); | |
575 | |
576 case EVR_UI: // unique identifier | |
577 return new DcmUniqueIdentifier(key); | |
578 | |
579 case EVR_ST: // short text | |
580 return new DcmShortText(key); | |
581 | |
582 case EVR_LO: // long string | |
583 return new DcmLongString(key); | |
584 | |
585 case EVR_LT: // long text | |
586 return new DcmLongText(key); | |
587 | |
588 case EVR_UT: // unlimited text | |
589 return new DcmUnlimitedText(key); | |
590 | |
591 case EVR_SH: // short string | |
592 return new DcmShortString(key); | |
593 | |
594 case EVR_PN: // person name | |
595 return new DcmPersonName(key); | |
596 | |
597 | |
598 /** | |
599 * Numerical types | |
600 **/ | |
601 | |
602 case EVR_SL: // signed long | |
603 return new DcmSignedLong(key); | |
604 | |
605 case EVR_SS: // signed short | |
606 return new DcmSignedShort(key); | |
607 | |
608 case EVR_UL: // unsigned long | |
609 return new DcmUnsignedLong(key); | |
610 | |
611 case EVR_US: // unsigned short | |
612 return new DcmUnsignedShort(key); | |
613 | |
614 case EVR_FL: // float single-precision | |
615 return new DcmFloatingPointSingle(key); | |
616 | |
617 case EVR_FD: // float double-precision | |
618 return new DcmFloatingPointDouble(key); | |
619 | |
620 | |
621 /** | |
622 * Sequence types, should never occur at this point. | |
623 **/ | |
624 | |
625 case EVR_SQ: // sequence of items | |
626 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
627 | |
628 | |
629 /** | |
630 * Internal to DCMTK. | |
631 **/ | |
632 | |
633 case EVR_ox: // OB or OW depending on context | |
634 case EVR_xs: // SS or US depending on context | |
635 case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name) | |
636 case EVR_na: // na="not applicable", for data which has no VR | |
637 case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor | |
638 case EVR_item: // used internally for items | |
639 case EVR_metainfo: // used internally for meta info datasets | |
640 case EVR_dataset: // used internally for datasets | |
641 case EVR_fileFormat: // used internally for DICOM files | |
642 case EVR_dicomDir: // used internally for DICOMDIR objects | |
643 case EVR_dirRecord: // used internally for DICOMDIR records | |
644 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image | |
645 case EVR_pixelItem: // used internally for pixel items in a compressed image | |
646 case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR) | |
647 case EVR_PixelData: // used internally for uncompressed pixeld data | |
648 case EVR_OverlayData: // used internally for overlay data | |
649 case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR | |
650 default: | |
651 break; | |
652 } | |
653 | |
654 throw OrthancException(ErrorCode_InternalError); | |
655 } | |
656 | |
657 | |
658 | |
659 static void FillElementWithString(DcmElement& element, | |
660 const DicomTag& tag, | |
661 const std::string& value) | |
662 { | |
663 DcmTag key(tag.GetGroup(), tag.GetElement()); | |
664 bool ok = false; | |
665 | |
666 try | |
667 { | |
668 switch (key.getEVR()) | |
669 { | |
670 // http://support.dcmtk.org/docs/dcvr_8h-source.html | |
671 | |
672 /** | |
673 * TODO. | |
674 **/ | |
675 | |
676 case EVR_OB: // other byte | |
677 case EVR_OF: // other float | |
678 case EVR_OW: // other word | |
679 case EVR_AT: // attribute tag | |
680 throw OrthancException(ErrorCode_NotImplemented); | |
681 | |
682 case EVR_UN: // unknown value representation | |
683 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
684 | |
685 | |
686 /** | |
687 * String types. | |
688 **/ | |
689 | |
690 case EVR_DS: // decimal string | |
691 case EVR_IS: // integer string | |
692 case EVR_AS: // age string | |
693 case EVR_DA: // date string | |
694 case EVR_DT: // date time string | |
695 case EVR_TM: // time string | |
696 case EVR_AE: // application entity title | |
697 case EVR_CS: // code string | |
698 case EVR_SH: // short string | |
699 case EVR_LO: // long string | |
700 case EVR_ST: // short text | |
701 case EVR_LT: // long text | |
702 case EVR_UT: // unlimited text | |
703 case EVR_PN: // person name | |
704 case EVR_UI: // unique identifier | |
705 { | |
706 ok = element.putString(value.c_str()).good(); | |
707 break; | |
708 } | |
709 | |
710 | |
711 /** | |
712 * Numerical types | |
713 **/ | |
714 | |
715 case EVR_SL: // signed long | |
716 { | |
717 ok = element.putSint32(boost::lexical_cast<Sint32>(value)).good(); | |
718 break; | |
719 } | |
720 | |
721 case EVR_SS: // signed short | |
722 { | |
723 ok = element.putSint16(boost::lexical_cast<Sint16>(value)).good(); | |
724 break; | |
725 } | |
726 | |
727 case EVR_UL: // unsigned long | |
728 { | |
729 ok = element.putUint32(boost::lexical_cast<Uint32>(value)).good(); | |
730 break; | |
731 } | |
732 | |
733 case EVR_US: // unsigned short | |
734 { | |
735 ok = element.putUint16(boost::lexical_cast<Uint16>(value)).good(); | |
736 break; | |
737 } | |
738 | |
739 case EVR_FL: // float single-precision | |
740 { | |
741 ok = element.putFloat32(boost::lexical_cast<float>(value)).good(); | |
742 break; | |
743 } | |
744 | |
745 case EVR_FD: // float double-precision | |
746 { | |
747 ok = element.putFloat64(boost::lexical_cast<double>(value)).good(); | |
748 break; | |
749 } | |
750 | |
751 | |
752 /** | |
753 * Sequence types, should never occur at this point. | |
754 **/ | |
755 | |
756 case EVR_SQ: // sequence of items | |
757 { | |
758 ok = false; | |
759 break; | |
760 } | |
761 | |
762 | |
763 /** | |
764 * Internal to DCMTK. | |
765 **/ | |
766 | |
767 case EVR_ox: // OB or OW depending on context | |
768 case EVR_xs: // SS or US depending on context | |
769 case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name) | |
770 case EVR_na: // na="not applicable", for data which has no VR | |
771 case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor | |
772 case EVR_item: // used internally for items | |
773 case EVR_metainfo: // used internally for meta info datasets | |
774 case EVR_dataset: // used internally for datasets | |
775 case EVR_fileFormat: // used internally for DICOM files | |
776 case EVR_dicomDir: // used internally for DICOMDIR objects | |
777 case EVR_dirRecord: // used internally for DICOMDIR records | |
778 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image | |
779 case EVR_pixelItem: // used internally for pixel items in a compressed image | |
780 case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR) | |
781 case EVR_PixelData: // used internally for uncompressed pixeld data | |
782 case EVR_OverlayData: // used internally for overlay data | |
783 case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR | |
784 default: | |
785 break; | |
786 } | |
787 } | |
788 catch (boost::bad_lexical_cast&) | |
789 { | |
790 ok = false; | |
791 } | |
792 | |
793 if (!ok) | |
794 { | |
795 throw OrthancException(ErrorCode_InternalError); | |
796 } | |
797 } | |
798 | |
799 | |
800 void ParsedDicomFile::Remove(const DicomTag& tag) | 520 void ParsedDicomFile::Remove(const DicomTag& tag) |
801 { | 521 { |
802 DcmTagKey key(tag.GetGroup(), tag.GetElement()); | 522 DcmTagKey key(tag.GetGroup(), tag.GetElement()); |
803 DcmElement* element = pimpl_->file_->getDataset()->remove(key); | 523 DcmElement* element = pimpl_->file_->getDataset()->remove(key); |
804 if (element != NULL) | 524 if (element != NULL) |
821 { | 541 { |
822 DcmElement* element = dataset.getElement(i); | 542 DcmElement* element = dataset.getElement(i); |
823 DcmTag tag(element->getTag()); | 543 DcmTag tag(element->getTag()); |
824 | 544 |
825 // Is this a private tag? | 545 // Is this a private tag? |
826 if (FromDcmtkBridge::IsPrivateTag(tag)) | 546 if (tag.isPrivate()) |
827 { | 547 { |
828 bool remove = true; | 548 bool remove = true; |
829 | 549 |
830 // Check whether this private tag is to be kept | 550 // Check whether this private tag is to be kept |
831 if (toKeep != NULL) | 551 if (toKeep != NULL) |
855 } | 575 } |
856 } | 576 } |
857 } | 577 } |
858 | 578 |
859 | 579 |
580 static void InsertInternal(DcmDataset& dicom, | |
581 DcmElement* element) | |
582 { | |
583 OFCondition cond = dicom.insert(element, false, false); | |
584 if (!cond.good()) | |
585 { | |
586 // This field already exists | |
587 delete element; | |
588 throw OrthancException(ErrorCode_InternalError); | |
589 } | |
590 } | |
860 | 591 |
861 | 592 |
862 void ParsedDicomFile::Insert(const DicomTag& tag, | 593 void ParsedDicomFile::Insert(const DicomTag& tag, |
863 const std::string& value) | 594 const Json::Value& value, |
864 { | 595 bool decodeBinaryTags) |
865 OFCondition cond; | 596 { |
866 | 597 std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeBinaryTags, GetEncoding())); |
867 if (FromDcmtkBridge::IsPrivateTag(tag) || | 598 InsertInternal(*pimpl_->file_->getDataset(), element.release()); |
868 FromDcmtkBridge::IsUnknownTag(tag)) | 599 } |
869 { | 600 |
870 // This is a private tag | 601 |
871 // http://support.dcmtk.org/redmine/projects/dcmtk/wiki/howto_addprivatedata | 602 static void ReplaceInternal(DcmDataset& dicom, |
872 | 603 std::auto_ptr<DcmElement>& element, |
873 DcmTag key(tag.GetGroup(), tag.GetElement(), EVR_OB); | 604 DicomReplaceMode mode) |
874 cond = pimpl_->file_->getDataset()->putAndInsertUint8Array | 605 { |
875 (key, (const Uint8*) value.c_str(), value.size(), false); | 606 const DcmTagKey& tag = element->getTag(); |
876 } | 607 |
877 else | 608 if (!dicom.findAndDeleteElement(tag).good()) |
878 { | |
879 std::auto_ptr<DcmElement> element(CreateElementForTag(tag)); | |
880 FillElementWithString(*element, tag, value); | |
881 | |
882 cond = pimpl_->file_->getDataset()->insert(element.release(), false, false); | |
883 } | |
884 | |
885 if (!cond.good()) | |
886 { | |
887 // This field already exists | |
888 throw OrthancException(ErrorCode_InternalError); | |
889 } | |
890 } | |
891 | |
892 | |
893 void ParsedDicomFile::Replace(const DicomTag& tag, | |
894 const std::string& value, | |
895 DicomReplaceMode mode) | |
896 { | |
897 DcmTagKey key(tag.GetGroup(), tag.GetElement()); | |
898 DcmElement* element = NULL; | |
899 | |
900 if (!pimpl_->file_->getDataset()->findAndGetElement(key, element).good() || | |
901 element == NULL) | |
902 { | 609 { |
903 // This field does not exist, act wrt. the specified "mode" | 610 // This field does not exist, act wrt. the specified "mode" |
904 switch (mode) | 611 switch (mode) |
905 { | 612 { |
906 case DicomReplaceMode_InsertIfAbsent: | 613 case DicomReplaceMode_InsertIfAbsent: |
907 Insert(tag, value); | |
908 break; | 614 break; |
909 | 615 |
910 case DicomReplaceMode_ThrowIfAbsent: | 616 case DicomReplaceMode_ThrowIfAbsent: |
911 throw OrthancException(ErrorCode_InexistentItem); | 617 throw OrthancException(ErrorCode_InexistentItem); |
912 | 618 |
913 case DicomReplaceMode_IgnoreIfAbsent: | 619 case DicomReplaceMode_IgnoreIfAbsent: |
914 return; | 620 return; |
915 } | 621 } |
916 } | 622 } |
623 | |
624 // Either the tag was not existing, or the replace mode was set to | |
625 // "InsertIfAbsent" | |
626 InsertInternal(dicom, element.release()); | |
627 } | |
628 | |
629 | |
630 void ParsedDicomFile::UpdateStorageUid(const DicomTag& tag, | |
631 const std::string& utf8Value, | |
632 bool decodeBinaryTags) | |
633 { | |
634 if (tag != DICOM_TAG_SOP_CLASS_UID && | |
635 tag != DICOM_TAG_SOP_INSTANCE_UID) | |
636 { | |
637 return; | |
638 } | |
639 | |
640 std::string binary; | |
641 const std::string* decoded = &utf8Value; | |
642 | |
643 if (decodeBinaryTags && | |
644 boost::starts_with(utf8Value, "data:application/octet-stream;base64,")) | |
645 { | |
646 std::string mime; | |
647 Toolbox::DecodeDataUriScheme(mime, binary, utf8Value); | |
648 decoded = &binary; | |
649 } | |
917 else | 650 else |
918 { | 651 { |
919 if (FromDcmtkBridge::IsPrivateTag(tag) || | 652 Encoding encoding = GetEncoding(); |
920 FromDcmtkBridge::IsUnknownTag(tag)) | 653 if (GetEncoding() != Encoding_Utf8) |
921 { | 654 { |
922 if (!element->putUint8Array((const Uint8*) value.c_str(), value.size()).good()) | 655 binary = Toolbox::ConvertFromUtf8(utf8Value, encoding); |
923 { | 656 decoded = &binary; |
924 throw OrthancException(ErrorCode_InternalError); | 657 } |
925 } | 658 } |
926 } | |
927 else | |
928 { | |
929 FillElementWithString(*element, tag, value); | |
930 } | |
931 } | |
932 | |
933 | 659 |
934 /** | 660 /** |
935 * dcmodify will automatically correct 'Media Storage SOP Class | 661 * dcmodify will automatically correct 'Media Storage SOP Class |
936 * UID' and 'Media Storage SOP Instance UID' in the metaheader, if | 662 * UID' and 'Media Storage SOP Instance UID' in the metaheader, if |
937 * you make changes to the related tags in the dataset ('SOP Class | 663 * you make changes to the related tags in the dataset ('SOP Class |
940 * option. | 666 * option. |
941 **/ | 667 **/ |
942 | 668 |
943 if (tag == DICOM_TAG_SOP_CLASS_UID) | 669 if (tag == DICOM_TAG_SOP_CLASS_UID) |
944 { | 670 { |
945 Replace(DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID, value, DicomReplaceMode_InsertIfAbsent); | 671 Replace(DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID, *decoded, DicomReplaceMode_InsertIfAbsent); |
946 } | 672 } |
947 | 673 |
948 if (tag == DICOM_TAG_SOP_INSTANCE_UID) | 674 if (tag == DICOM_TAG_SOP_INSTANCE_UID) |
949 { | 675 { |
950 Replace(DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID, value, DicomReplaceMode_InsertIfAbsent); | 676 Replace(DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID, *decoded, DicomReplaceMode_InsertIfAbsent); |
677 } | |
678 } | |
679 | |
680 | |
681 void ParsedDicomFile::Replace(const DicomTag& tag, | |
682 const std::string& utf8Value, | |
683 DicomReplaceMode mode) | |
684 { | |
685 std::auto_ptr<DcmElement> element(FromDcmtkBridge::CreateElementForTag(tag)); | |
686 FromDcmtkBridge::FillElementWithString(*element, tag, utf8Value, false, GetEncoding()); | |
687 ReplaceInternal(*pimpl_->file_->getDataset(), element, mode); | |
688 UpdateStorageUid(tag, utf8Value, false); | |
689 } | |
690 | |
691 | |
692 void ParsedDicomFile::Replace(const DicomTag& tag, | |
693 const Json::Value& value, | |
694 bool decodeBinaryTags, | |
695 DicomReplaceMode mode) | |
696 { | |
697 std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeBinaryTags, GetEncoding())); | |
698 ReplaceInternal(*pimpl_->file_->getDataset(), element, mode); | |
699 | |
700 if (tag == DICOM_TAG_SOP_CLASS_UID || | |
701 tag == DICOM_TAG_SOP_INSTANCE_UID) | |
702 { | |
703 if (value.type() != Json::stringValue) | |
704 { | |
705 throw OrthancException(ErrorCode_BadParameterType); | |
706 } | |
707 | |
708 UpdateStorageUid(tag, value.asString(), decodeBinaryTags); | |
951 } | 709 } |
952 } | 710 } |
953 | 711 |
954 | 712 |
955 void ParsedDicomFile::Answer(RestApiOutput& output) | 713 void ParsedDicomFile::Answer(RestApiOutput& output) |
1003 element == NULL) | 761 element == NULL) |
1004 { | 762 { |
1005 return false; | 763 return false; |
1006 } | 764 } |
1007 | 765 |
1008 std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(*element, pimpl_->encoding_)); | 766 std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(*element, GetEncoding())); |
1009 | 767 |
1010 if (v.get() == NULL) | 768 if (v.get() == NULL) |
1011 { | 769 { |
1012 value = ""; | 770 value = ""; |
1013 } | 771 } |
1083 | 841 |
1084 | 842 |
1085 ParsedDicomFile::ParsedDicomFile() : pimpl_(new PImpl) | 843 ParsedDicomFile::ParsedDicomFile() : pimpl_(new PImpl) |
1086 { | 844 { |
1087 pimpl_->file_.reset(new DcmFileFormat); | 845 pimpl_->file_.reset(new DcmFileFormat); |
1088 pimpl_->encoding_ = Encoding_Ascii; | |
1089 Replace(DICOM_TAG_PATIENT_ID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Patient)); | 846 Replace(DICOM_TAG_PATIENT_ID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Patient)); |
1090 Replace(DICOM_TAG_STUDY_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Study)); | 847 Replace(DICOM_TAG_STUDY_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Study)); |
1091 Replace(DICOM_TAG_SERIES_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Series)); | 848 Replace(DICOM_TAG_SERIES_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Series)); |
1092 Replace(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance)); | 849 Replace(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance)); |
1093 } | 850 } |
1113 | 870 |
1114 ParsedDicomFile::ParsedDicomFile(ParsedDicomFile& other) : | 871 ParsedDicomFile::ParsedDicomFile(ParsedDicomFile& other) : |
1115 pimpl_(new PImpl) | 872 pimpl_(new PImpl) |
1116 { | 873 { |
1117 pimpl_->file_.reset(dynamic_cast<DcmFileFormat*>(other.pimpl_->file_->clone())); | 874 pimpl_->file_.reset(dynamic_cast<DcmFileFormat*>(other.pimpl_->file_->clone())); |
1118 pimpl_->encoding_ = other.pimpl_->encoding_; | |
1119 | 875 |
1120 // Create a new instance-level identifier | 876 // Create a new instance-level identifier |
1121 Replace(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance)); | 877 Replace(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance)); |
1122 } | 878 } |
1123 | 879 |
1154 { | 910 { |
1155 EmbedPdf(content); | 911 EmbedPdf(content); |
1156 } | 912 } |
1157 else | 913 else |
1158 { | 914 { |
1159 LOG(ERROR) << "Unsupported MIME type for the content of a new DICOM file"; | 915 LOG(ERROR) << "Unsupported MIME type for the content of a new DICOM file: " << mime; |
1160 throw OrthancException(ErrorCode_NotImplemented); | 916 throw OrthancException(ErrorCode_NotImplemented); |
1161 } | 917 } |
1162 } | 918 } |
1163 | 919 |
1164 | 920 |
1343 } | 1099 } |
1344 | 1100 |
1345 | 1101 |
1346 Encoding ParsedDicomFile::GetEncoding() const | 1102 Encoding ParsedDicomFile::GetEncoding() const |
1347 { | 1103 { |
1348 return pimpl_->encoding_; | 1104 return FromDcmtkBridge::DetectEncoding(*pimpl_->file_->getDataset()); |
1349 } | 1105 } |
1350 | 1106 |
1351 | 1107 |
1352 void ParsedDicomFile::SetEncoding(Encoding encoding) | 1108 void ParsedDicomFile::SetEncoding(Encoding encoding) |
1353 { | 1109 { |
1356 // This Cyrillic codepage is not officially supported by the | 1112 // This Cyrillic codepage is not officially supported by the |
1357 // DICOM standard. Do not set the SpecificCharacterSet tag. | 1113 // DICOM standard. Do not set the SpecificCharacterSet tag. |
1358 return; | 1114 return; |
1359 } | 1115 } |
1360 | 1116 |
1361 pimpl_->encoding_ = encoding; | |
1362 | |
1363 std::string s = GetDicomSpecificCharacterSet(encoding); | 1117 std::string s = GetDicomSpecificCharacterSet(encoding); |
1364 Replace(DICOM_TAG_SPECIFIC_CHARACTER_SET, s, DicomReplaceMode_InsertIfAbsent); | 1118 Replace(DICOM_TAG_SPECIFIC_CHARACTER_SET, s, DicomReplaceMode_InsertIfAbsent); |
1365 } | 1119 } |
1366 | 1120 |
1367 void ParsedDicomFile::ToJson(Json::Value& target, bool simplify) | 1121 void ParsedDicomFile::ToJson(Json::Value& target, |
1368 { | 1122 DicomToJsonFormat format, |
1369 if (simplify) | 1123 unsigned int maxStringLength) |
1370 { | 1124 { |
1371 Json::Value tmp; | 1125 FromDcmtkBridge::ToJson(target, *pimpl_->file_->getDataset(), format, maxStringLength); |
1372 FromDcmtkBridge::ToJson(tmp, *pimpl_->file_->getDataset()); | |
1373 Toolbox::SimplifyTags(target, tmp); | |
1374 } | |
1375 else | |
1376 { | |
1377 FromDcmtkBridge::ToJson(target, *pimpl_->file_->getDataset()); | |
1378 } | |
1379 } | 1126 } |
1380 | 1127 |
1381 | 1128 |
1382 bool ParsedDicomFile::HasTag(const DicomTag& tag) const | 1129 bool ParsedDicomFile::HasTag(const DicomTag& tag) const |
1383 { | 1130 { |