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 #include "../PrecompiledHeaders.h" 24 #include "DicomValue.h" 25 26 #include "../OrthancException.h" 27 #include "../SerializationToolbox.h" 28 #include "../Toolbox.h" 29 30 #include <boost/lexical_cast.hpp> 31 32 namespace Orthanc 33 { DicomValue()34 DicomValue::DicomValue() : 35 type_(Type_Null) 36 { 37 } 38 39 DicomValue(const DicomValue & other)40 DicomValue::DicomValue(const DicomValue& other) : 41 type_(other.type_), 42 content_(other.content_) 43 { 44 } 45 46 DicomValue(const std::string & content,bool isBinary)47 DicomValue::DicomValue(const std::string& content, 48 bool isBinary) : 49 type_(isBinary ? Type_Binary : Type_String), 50 content_(content) 51 { 52 } 53 54 DicomValue(const char * data,size_t size,bool isBinary)55 DicomValue::DicomValue(const char* data, 56 size_t size, 57 bool isBinary) : 58 type_(isBinary ? Type_Binary : Type_String) 59 { 60 content_.assign(data, size); 61 } 62 63 GetContent() const64 const std::string& DicomValue::GetContent() const 65 { 66 if (type_ == Type_Null) 67 { 68 throw OrthancException(ErrorCode_BadParameterType); 69 } 70 else 71 { 72 return content_; 73 } 74 } 75 IsNull() const76 bool DicomValue::IsNull() const 77 { 78 return type_ == Type_Null; 79 } 80 IsBinary() const81 bool DicomValue::IsBinary() const 82 { 83 return type_ == Type_Binary; 84 } 85 86 Clone() const87 DicomValue* DicomValue::Clone() const 88 { 89 return new DicomValue(*this); 90 } 91 92 93 #if ORTHANC_ENABLE_BASE64 == 1 FormatDataUriScheme(std::string & target,const std::string & mime) const94 void DicomValue::FormatDataUriScheme(std::string& target, 95 const std::string& mime) const 96 { 97 Toolbox::EncodeBase64(target, GetContent()); 98 target.insert(0, "data:" + mime + ";base64,"); 99 } 100 FormatDataUriScheme(std::string & target) const101 void DicomValue::FormatDataUriScheme(std::string& target) const 102 { 103 FormatDataUriScheme(target, MIME_BINARY); 104 } 105 #endif 106 107 // same as ParseValue but in case the value actually contains a sequence, 108 // it will return the first value 109 // this has been introduced to support invalid "width/height" DICOM tags in some US 110 // images where the width is stored as "800\0" ! 111 template <typename T, 112 bool allowSigned> ParseFirstValue(T & result,const DicomValue & source)113 static bool ParseFirstValue(T& result, 114 const DicomValue& source) 115 { 116 if (source.IsBinary() || 117 source.IsNull()) 118 { 119 return false; 120 } 121 122 try 123 { 124 std::string value = Toolbox::StripSpaces(source.GetContent()); 125 if (value.empty()) 126 { 127 return false; 128 } 129 130 if (!allowSigned && 131 value[0] == '-') 132 { 133 return false; 134 } 135 136 if (value.find("\\") == std::string::npos) 137 { 138 result = boost::lexical_cast<T>(value); 139 return true; 140 } 141 else 142 { 143 std::vector<std::string> tokens; 144 Toolbox::TokenizeString(tokens, value, '\\'); 145 146 if (tokens.size() >= 1) 147 { 148 result = boost::lexical_cast<T>(tokens[0]); 149 return true; 150 } 151 152 return false; 153 } 154 } 155 catch (boost::bad_lexical_cast&) 156 { 157 return false; 158 } 159 } 160 161 162 template <typename T, 163 bool allowSigned> ParseValue(T & result,const DicomValue & source)164 static bool ParseValue(T& result, 165 const DicomValue& source) 166 { 167 if (source.IsBinary() || 168 source.IsNull()) 169 { 170 return false; 171 } 172 173 try 174 { 175 std::string value = Toolbox::StripSpaces(source.GetContent()); 176 if (value.empty()) 177 { 178 return false; 179 } 180 181 if (!allowSigned && 182 value[0] == '-') 183 { 184 return false; 185 } 186 187 result = boost::lexical_cast<T>(value); 188 return true; 189 } 190 catch (boost::bad_lexical_cast&) 191 { 192 return false; 193 } 194 } 195 ParseInteger32(int32_t & result) const196 bool DicomValue::ParseInteger32(int32_t& result) const 197 { 198 int64_t tmp; 199 if (ParseValue<int64_t, true>(tmp, *this)) 200 { 201 result = static_cast<int32_t>(tmp); 202 return (tmp == static_cast<int64_t>(result)); // Check no overflow occurs 203 } 204 else 205 { 206 return false; 207 } 208 } 209 ParseInteger64(int64_t & result) const210 bool DicomValue::ParseInteger64(int64_t& result) const 211 { 212 return ParseValue<int64_t, true>(result, *this); 213 } 214 ParseUnsignedInteger32(uint32_t & result) const215 bool DicomValue::ParseUnsignedInteger32(uint32_t& result) const 216 { 217 uint64_t tmp; 218 if (ParseValue<uint64_t, false>(tmp, *this)) 219 { 220 result = static_cast<uint32_t>(tmp); 221 return (tmp == static_cast<uint64_t>(result)); // Check no overflow occurs 222 } 223 else 224 { 225 return false; 226 } 227 } 228 ParseUnsignedInteger64(uint64_t & result) const229 bool DicomValue::ParseUnsignedInteger64(uint64_t& result) const 230 { 231 return ParseValue<uint64_t, false>(result, *this); 232 } 233 ParseFloat(float & result) const234 bool DicomValue::ParseFloat(float& result) const 235 { 236 return ParseValue<float, true>(result, *this); 237 } 238 ParseDouble(double & result) const239 bool DicomValue::ParseDouble(double& result) const 240 { 241 return ParseValue<double, true>(result, *this); 242 } 243 ParseFirstFloat(float & result) const244 bool DicomValue::ParseFirstFloat(float& result) const 245 { 246 return ParseFirstValue<float, true>(result, *this); 247 } 248 ParseFirstUnsignedInteger(unsigned int & result) const249 bool DicomValue::ParseFirstUnsignedInteger(unsigned int& result) const 250 { 251 return ParseFirstValue<unsigned int, true>(result, *this); 252 } 253 CopyToString(std::string & result,bool allowBinary) const254 bool DicomValue::CopyToString(std::string& result, 255 bool allowBinary) const 256 { 257 if (IsNull()) 258 { 259 return false; 260 } 261 else if (IsBinary() && !allowBinary) 262 { 263 return false; 264 } 265 else 266 { 267 result.assign(content_); 268 return true; 269 } 270 } 271 272 273 static const char* KEY_TYPE = "Type"; 274 static const char* KEY_CONTENT = "Content"; 275 Serialize(Json::Value & target) const276 void DicomValue::Serialize(Json::Value& target) const 277 { 278 target = Json::objectValue; 279 280 switch (type_) 281 { 282 case Type_Null: 283 target[KEY_TYPE] = "Null"; 284 break; 285 286 case Type_String: 287 target[KEY_TYPE] = "String"; 288 target[KEY_CONTENT] = content_; 289 break; 290 291 case Type_Binary: 292 { 293 target[KEY_TYPE] = "Binary"; 294 295 std::string base64; 296 Toolbox::EncodeBase64(base64, content_); 297 target[KEY_CONTENT] = base64; 298 break; 299 } 300 301 default: 302 throw OrthancException(ErrorCode_InternalError); 303 } 304 } 305 Unserialize(const Json::Value & source)306 void DicomValue::Unserialize(const Json::Value& source) 307 { 308 std::string type = SerializationToolbox::ReadString(source, KEY_TYPE); 309 310 if (type == "Null") 311 { 312 type_ = Type_Null; 313 content_.clear(); 314 } 315 else if (type == "String") 316 { 317 type_ = Type_String; 318 content_ = SerializationToolbox::ReadString(source, KEY_CONTENT); 319 } 320 else if (type == "Binary") 321 { 322 type_ = Type_Binary; 323 324 const std::string base64 =SerializationToolbox::ReadString(source, KEY_CONTENT); 325 Toolbox::DecodeBase64(content_, base64); 326 } 327 else 328 { 329 throw OrthancException(ErrorCode_BadFileFormat); 330 } 331 } 332 } 333