1 /** 2 * Orthanc - A Lightweight, RESTful DICOM Store 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics 4 * Department, University Hospital of Liege, Belgium 5 * Copyright (C) 2017-2021 Osimis S.A., Belgium 6 * 7 * This program is free software: you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public License 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this program. If not, see 19 * <http://www.gnu.org/licenses/>. 20 **/ 21 22 23 #pragma once 24 25 #include "DicomTag.h" 26 #include "StreamBlockReader.h" 27 28 namespace Orthanc 29 { 30 /** 31 * This class parses a stream containing a DICOM instance, using a 32 * state machine. 33 * 34 * It does *not* support the visit of sequences (it only works at 35 * the first level of the hierarchy), and as a consequence, it 36 * doesn't give access to the pixel data of compressed transfer 37 * syntaxes. 38 **/ 39 class ORTHANC_PUBLIC DicomStreamReader : public boost::noncopyable 40 { 41 public: 42 class IVisitor : public boost::noncopyable 43 { 44 public: ~IVisitor()45 virtual ~IVisitor() 46 { 47 } 48 49 // The data from this function will always be Little Endian (as 50 // specified by the DICOM standard) 51 virtual void VisitMetaHeaderTag(const DicomTag& tag, 52 const ValueRepresentation& vr, 53 const std::string& value) = 0; 54 55 virtual void VisitTransferSyntax(DicomTransferSyntax transferSyntax) = 0; 56 57 // Return "false" to stop processing 58 virtual bool VisitDatasetTag(const DicomTag& tag, 59 const ValueRepresentation& vr, 60 const std::string& value, 61 bool isLittleEndian, 62 uint64_t fileOffset) = 0; 63 }; 64 65 private: 66 class PixelDataVisitor; 67 68 enum State 69 { 70 State_Preamble, 71 State_MetaHeader, 72 State_DatasetTag, 73 State_SequenceExplicitLength, 74 State_SequenceExplicitValue, 75 State_DatasetExplicitLength, 76 State_DatasetValue, 77 State_Done 78 }; 79 80 StreamBlockReader reader_; 81 State state_; 82 DicomTransferSyntax transferSyntax_; 83 DicomTag danglingTag_; // Current root-level tag 84 ValueRepresentation danglingVR_; 85 uint64_t danglingOffset_; 86 unsigned int sequenceDepth_; 87 88 bool IsLittleEndian() const; 89 90 void HandlePreamble(IVisitor& visitor, 91 const std::string& block); 92 93 void HandleMetaHeader(IVisitor& visitor, 94 const std::string& block); 95 96 void HandleDatasetTag(const std::string& block, 97 const DicomTag& untilTag); 98 99 void HandleDatasetExplicitLength(uint32_t length); 100 101 void HandleDatasetExplicitLength(IVisitor& visitor, 102 const std::string& block); 103 104 void HandleSequenceExplicitLength(const std::string& block); 105 106 void HandleSequenceExplicitValue(); 107 108 void HandleDatasetValue(IVisitor& visitor, 109 const std::string& block); 110 111 public: 112 explicit DicomStreamReader(std::istream& stream); 113 114 /** 115 * Consume all the available bytes from the input stream, until 116 * end-of-stream is reached or the current tag is ">= untilTag". 117 * This method can be invoked several times, as more bytes are 118 * available from the input stream. To check if the DICOM stream 119 * is fully parsed until the goal tag, call "IsDone()". 120 **/ 121 void Consume(IVisitor& visitor, 122 const DicomTag& untilTag); 123 124 void Consume(IVisitor& visitor); 125 126 bool IsDone() const; 127 128 uint64_t GetProcessedBytes() const; 129 130 static bool LookupPixelDataOffset(uint64_t& offset, 131 const std::string& dicom); 132 133 static bool LookupPixelDataOffset(uint64_t& offset, 134 const void* buffer, 135 size_t size); 136 }; 137 } 138