comparison Resources/Orthanc/OrthancServer/FromDcmtkBridge.cpp @ 59:7a3853d51c45

Move "Framework/Orthanc/" as "Resources/Orthanc/"
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 25 Nov 2016 16:34:34 +0100
parents Framework/Orthanc/OrthancServer/FromDcmtkBridge.cpp@06847108819c
children 5b127ab0080b
comparison
equal deleted inserted replaced
58:35468714a38e 59:7a3853d51c45
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 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * In addition, as a special exception, the copyright holders of this
12 * program give permission to link the code of its release with the
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it
14 * that use the same license as the "OpenSSL" library), and distribute
15 * the linked executables. You must obey the GNU General Public License
16 * in all respects for all of the code used other than "OpenSSL". If you
17 * modify file(s) with this exception, you may extend this exception to
18 * your version of the file(s), but you are not obligated to do so. If
19 * you do not wish to do so, delete this exception statement from your
20 * version. If you delete this exception statement from all source files
21 * in the program, then also delete it here.
22 *
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 * General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 **/
31
32
33 #include "PrecompiledHeadersServer.h"
34
35 #ifndef NOMINMAX
36 #define NOMINMAX
37 #endif
38
39 #include "FromDcmtkBridge.h"
40 #include "ToDcmtkBridge.h"
41 #include "../Core/Logging.h"
42 #include "../Core/SystemToolbox.h"
43 #include "../Core/Toolbox.h"
44 #include "../Core/TemporaryFile.h"
45 #include "../Core/OrthancException.h"
46
47 #include <list>
48 #include <limits>
49
50 #include <boost/lexical_cast.hpp>
51 #include <boost/filesystem.hpp>
52 #include <boost/algorithm/string/predicate.hpp>
53
54 #include <dcmtk/dcmdata/dcdeftag.h>
55 #include <dcmtk/dcmdata/dcdicent.h>
56 #include <dcmtk/dcmdata/dcdict.h>
57 #include <dcmtk/dcmdata/dcfilefo.h>
58 #include <dcmtk/dcmdata/dcostrmb.h>
59 #include <dcmtk/dcmdata/dcpixel.h>
60 #include <dcmtk/dcmdata/dcuid.h>
61 #include <dcmtk/dcmdata/dcistrmb.h>
62
63 #include <dcmtk/dcmdata/dcvrae.h>
64 #include <dcmtk/dcmdata/dcvras.h>
65 #include <dcmtk/dcmdata/dcvrat.h>
66 #include <dcmtk/dcmdata/dcvrcs.h>
67 #include <dcmtk/dcmdata/dcvrda.h>
68 #include <dcmtk/dcmdata/dcvrds.h>
69 #include <dcmtk/dcmdata/dcvrdt.h>
70 #include <dcmtk/dcmdata/dcvrfd.h>
71 #include <dcmtk/dcmdata/dcvrfl.h>
72 #include <dcmtk/dcmdata/dcvris.h>
73 #include <dcmtk/dcmdata/dcvrlo.h>
74 #include <dcmtk/dcmdata/dcvrlt.h>
75 #include <dcmtk/dcmdata/dcvrpn.h>
76 #include <dcmtk/dcmdata/dcvrsh.h>
77 #include <dcmtk/dcmdata/dcvrsl.h>
78 #include <dcmtk/dcmdata/dcvrss.h>
79 #include <dcmtk/dcmdata/dcvrst.h>
80 #include <dcmtk/dcmdata/dcvrtm.h>
81 #include <dcmtk/dcmdata/dcvrui.h>
82 #include <dcmtk/dcmdata/dcvrul.h>
83 #include <dcmtk/dcmdata/dcvrus.h>
84 #include <dcmtk/dcmdata/dcvrut.h>
85
86
87 #if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
88 #include <EmbeddedResources.h>
89 #endif
90
91
92 namespace Orthanc
93 {
94 static inline uint16_t GetCharValue(char c)
95 {
96 if (c >= '0' && c <= '9')
97 return c - '0';
98 else if (c >= 'a' && c <= 'f')
99 return c - 'a' + 10;
100 else if (c >= 'A' && c <= 'F')
101 return c - 'A' + 10;
102 else
103 return 0;
104 }
105
106 static inline uint16_t GetTagValue(const char* c)
107 {
108 return ((GetCharValue(c[0]) << 12) +
109 (GetCharValue(c[1]) << 8) +
110 (GetCharValue(c[2]) << 4) +
111 GetCharValue(c[3]));
112 }
113
114
115 #if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
116 static void LoadEmbeddedDictionary(DcmDataDictionary& dictionary,
117 EmbeddedResources::FileResourceId resource)
118 {
119 std::string content;
120 EmbeddedResources::GetFileResource(content, resource);
121
122 TemporaryFile tmp;
123 tmp.Write(content);
124
125 if (!dictionary.loadDictionary(tmp.GetPath().c_str()))
126 {
127 LOG(ERROR) << "Cannot read embedded dictionary. Under Windows, make sure that "
128 << "your TEMP directory does not contain special characters.";
129 throw OrthancException(ErrorCode_InternalError);
130 }
131 }
132
133 #else
134 static void LoadExternalDictionary(DcmDataDictionary& dictionary,
135 const std::string& directory,
136 const std::string& filename)
137 {
138 boost::filesystem::path p = directory;
139 p = p / filename;
140
141 LOG(WARNING) << "Loading the external DICOM dictionary " << p;
142
143 if (!dictionary.loadDictionary(p.string().c_str()))
144 {
145 throw OrthancException(ErrorCode_InternalError);
146 }
147 }
148 #endif
149
150
151 namespace
152 {
153 class DictionaryLocker
154 {
155 private:
156 DcmDataDictionary& dictionary_;
157
158 public:
159 DictionaryLocker() : dictionary_(dcmDataDict.wrlock())
160 {
161 }
162
163 ~DictionaryLocker()
164 {
165 dcmDataDict.unlock();
166 }
167
168 DcmDataDictionary& operator*()
169 {
170 return dictionary_;
171 }
172
173 DcmDataDictionary* operator->()
174 {
175 return &dictionary_;
176 }
177 };
178 }
179
180
181 void FromDcmtkBridge::InitializeDictionary(bool loadPrivateDictionary)
182 {
183 {
184 DictionaryLocker locker;
185
186 locker->clear();
187
188 #if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
189 LOG(WARNING) << "Loading the embedded dictionaries";
190 /**
191 * Do not load DICONDE dictionary, it breaks the other tags. The
192 * command "strace storescu 2>&1 |grep dic" shows that DICONDE
193 * dictionary is not loaded by storescu.
194 **/
195 //LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_DICONDE);
196
197 LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_DICOM);
198
199 if (loadPrivateDictionary)
200 {
201 LOG(INFO) << "Loading the embedded dictionary of private tags";
202 LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_PRIVATE);
203 }
204 else
205 {
206 LOG(INFO) << "The dictionary of private tags has not been loaded";
207 }
208
209 #elif defined(__linux__) || defined(__FreeBSD_kernel__)
210 std::string path = DCMTK_DICTIONARY_DIR;
211
212 const char* env = std::getenv(DCM_DICT_ENVIRONMENT_VARIABLE);
213 if (env != NULL)
214 {
215 path = std::string(env);
216 }
217
218 LoadExternalDictionary(*locker, path, "dicom.dic");
219
220 if (loadPrivateDictionary)
221 {
222 LoadExternalDictionary(*locker, path, "private.dic");
223 }
224 else
225 {
226 LOG(INFO) << "The dictionary of private tags has not been loaded";
227 }
228
229 #else
230 #error Support your platform here
231 #endif
232 }
233
234 /* make sure data dictionary is loaded */
235 if (!dcmDataDict.isDictionaryLoaded())
236 {
237 LOG(ERROR) << "No DICOM dictionary loaded, check environment variable: " << DCM_DICT_ENVIRONMENT_VARIABLE;
238 throw OrthancException(ErrorCode_InternalError);
239 }
240
241 {
242 // Test the dictionary with a simple DICOM tag
243 DcmTag key(0x0010, 0x1030); // This is PatientWeight
244 if (key.getEVR() != EVR_DS)
245 {
246 LOG(ERROR) << "The DICOM dictionary has not been correctly read";
247 throw OrthancException(ErrorCode_InternalError);
248 }
249 }
250 }
251
252
253 void FromDcmtkBridge::RegisterDictionaryTag(const DicomTag& tag,
254 ValueRepresentation vr,
255 const std::string& name,
256 unsigned int minMultiplicity,
257 unsigned int maxMultiplicity,
258 const std::string& privateCreator)
259 {
260 if (minMultiplicity < 1)
261 {
262 throw OrthancException(ErrorCode_ParameterOutOfRange);
263 }
264
265 bool arbitrary = false;
266 if (maxMultiplicity == 0)
267 {
268 maxMultiplicity = DcmVariableVM;
269 arbitrary = true;
270 }
271 else if (maxMultiplicity < minMultiplicity)
272 {
273 throw OrthancException(ErrorCode_ParameterOutOfRange);
274 }
275
276 DcmEVR evr = ToDcmtkBridge::Convert(vr);
277
278 LOG(INFO) << "Registering tag in dictionary: " << tag << " " << (DcmVR(evr).getValidVRName()) << " "
279 << name << " (multiplicity: " << minMultiplicity << "-"
280 << (arbitrary ? "n" : boost::lexical_cast<std::string>(maxMultiplicity)) << ")";
281
282 std::auto_ptr<DcmDictEntry> entry;
283 if (privateCreator.empty())
284 {
285 if (tag.GetGroup() % 2 == 1)
286 {
287 char buf[128];
288 sprintf(buf, "Warning: You are registering a private tag (%04x,%04x), "
289 "but no private creator was associated with it",
290 tag.GetGroup(), tag.GetElement());
291 LOG(WARNING) << buf;
292 }
293
294 entry.reset(new DcmDictEntry(tag.GetGroup(),
295 tag.GetElement(),
296 evr, name.c_str(),
297 static_cast<int>(minMultiplicity),
298 static_cast<int>(maxMultiplicity),
299 NULL /* version */,
300 OFTrue /* doCopyString */,
301 NULL /* private creator */));
302 }
303 else
304 {
305 // "Private Data Elements have an odd Group Number that is not
306 // (0001,eeee), (0003,eeee), (0005,eeee), (0007,eeee), or
307 // (FFFF,eeee)."
308 if (tag.GetGroup() % 2 == 0 /* even */ ||
309 tag.GetGroup() == 0x0001 ||
310 tag.GetGroup() == 0x0003 ||
311 tag.GetGroup() == 0x0005 ||
312 tag.GetGroup() == 0x0007 ||
313 tag.GetGroup() == 0xffff)
314 {
315 char buf[128];
316 sprintf(buf, "Trying to register private tag (%04x,%04x), but it must have an odd group >= 0x0009",
317 tag.GetGroup(), tag.GetElement());
318 LOG(ERROR) << buf;
319 throw OrthancException(ErrorCode_ParameterOutOfRange);
320 }
321
322 entry.reset(new DcmDictEntry(tag.GetGroup(),
323 tag.GetElement(),
324 evr, name.c_str(),
325 static_cast<int>(minMultiplicity),
326 static_cast<int>(maxMultiplicity),
327 "private" /* version */,
328 OFTrue /* doCopyString */,
329 privateCreator.c_str()));
330 }
331
332 entry->setGroupRangeRestriction(DcmDictRange_Unspecified);
333 entry->setElementRangeRestriction(DcmDictRange_Unspecified);
334
335 {
336 DictionaryLocker locker;
337
338 if (locker->findEntry(name.c_str()))
339 {
340 LOG(ERROR) << "Cannot register two tags with the same symbolic name \"" << name << "\"";
341 throw OrthancException(ErrorCode_AlreadyExistingTag);
342 }
343
344 locker->addEntry(entry.release());
345 }
346 }
347
348
349 Encoding FromDcmtkBridge::DetectEncoding(DcmItem& dataset,
350 Encoding defaultEncoding)
351 {
352 Encoding encoding = defaultEncoding;
353
354 OFString tmp;
355 if (dataset.findAndGetOFString(DCM_SpecificCharacterSet, tmp).good())
356 {
357 std::string characterSet = Toolbox::StripSpaces(std::string(tmp.c_str()));
358
359 if (characterSet.empty())
360 {
361 // Empty specific character set tag: Use the default encoding
362 }
363 else if (GetDicomEncoding(encoding, characterSet.c_str()))
364 {
365 // The specific character set is supported by the Orthanc core
366 }
367 else
368 {
369 LOG(WARNING) << "Value of Specific Character Set (0008,0005) is not supported: " << characterSet
370 << ", fallback to ASCII (remove all special characters)";
371 encoding = Encoding_Ascii;
372 }
373 }
374 else
375 {
376 // No specific character set tag: Use the default encoding
377 }
378
379 return encoding;
380 }
381
382
383 void FromDcmtkBridge::ExtractDicomSummary(DicomMap& target,
384 DcmItem& dataset,
385 unsigned int maxStringLength,
386 Encoding defaultEncoding)
387 {
388 Encoding encoding = DetectEncoding(dataset, defaultEncoding);
389
390 target.Clear();
391 for (unsigned long i = 0; i < dataset.card(); i++)
392 {
393 DcmElement* element = dataset.getElement(i);
394 if (element && element->isLeaf())
395 {
396 target.SetValue(element->getTag().getGTag(),
397 element->getTag().getETag(),
398 ConvertLeafElement(*element, DicomToJsonFlags_Default, maxStringLength, encoding));
399 }
400 }
401 }
402
403
404 DicomTag FromDcmtkBridge::Convert(const DcmTag& tag)
405 {
406 return DicomTag(tag.getGTag(), tag.getETag());
407 }
408
409
410 DicomTag FromDcmtkBridge::GetTag(const DcmElement& element)
411 {
412 return DicomTag(element.getGTag(), element.getETag());
413 }
414
415
416 DicomValue* FromDcmtkBridge::ConvertLeafElement(DcmElement& element,
417 DicomToJsonFlags flags,
418 unsigned int maxStringLength,
419 Encoding encoding)
420 {
421 if (!element.isLeaf())
422 {
423 // This function is only applicable to leaf elements
424 throw OrthancException(ErrorCode_BadParameterType);
425 }
426
427 char *c = NULL;
428 if (element.isaString() &&
429 element.getString(c).good())
430 {
431 if (c == NULL) // This case corresponds to the empty string
432 {
433 return new DicomValue("", false);
434 }
435 else
436 {
437 std::string s(c);
438 std::string utf8 = Toolbox::ConvertToUtf8(s, encoding);
439
440 if (maxStringLength != 0 &&
441 utf8.size() > maxStringLength)
442 {
443 return new DicomValue; // Too long, create a NULL value
444 }
445 else
446 {
447 return new DicomValue(utf8, false);
448 }
449 }
450 }
451
452
453 if (element.getVR() == EVR_UN)
454 {
455 // Unknown value representation: Lookup in the dictionary. This
456 // is notably the case for private tags registered with the
457 // "Dictionary" configuration option.
458 DictionaryLocker locker;
459
460 const DcmDictEntry* entry = locker->findEntry(element.getTag().getXTag(),
461 element.getTag().getPrivateCreator());
462 if (entry != NULL &&
463 entry->getVR().isaString())
464 {
465 Uint8* data = NULL;
466
467 // At (*), we do not try and convert to UTF-8, as nothing says
468 // the encoding of the private tag is the same as that of the
469 // remaining of the DICOM dataset. Only go for ASCII strings.
470
471 if (element.getUint8Array(data) == EC_Normal &&
472 Toolbox::IsAsciiString(data, element.getLength())) // (*)
473 {
474 if (data == NULL)
475 {
476 return new DicomValue("", false); // Empty string
477 }
478 else if (maxStringLength != 0 &&
479 element.getLength() > maxStringLength)
480 {
481 return new DicomValue; // Too long, create a NULL value
482 }
483 else
484 {
485 std::string s(reinterpret_cast<const char*>(data), element.getLength());
486 return new DicomValue(s, false);
487 }
488 }
489 }
490 }
491
492
493 try
494 {
495 // http://support.dcmtk.org/docs/dcvr_8h-source.html
496 switch (element.getVR())
497 {
498
499 /**
500 * Deal with binary data (including PixelData).
501 **/
502
503 case EVR_OB: // other byte
504 case EVR_OF: // other float
505 case EVR_OW: // other word
506 case EVR_UN: // unknown value representation
507 case EVR_ox: // OB or OW depending on context
508 case EVR_DS: // decimal string
509 case EVR_IS: // integer string
510 case EVR_AS: // age string
511 case EVR_DA: // date string
512 case EVR_DT: // date time string
513 case EVR_TM: // time string
514 case EVR_AE: // application entity title
515 case EVR_CS: // code string
516 case EVR_SH: // short string
517 case EVR_LO: // long string
518 case EVR_ST: // short text
519 case EVR_LT: // long text
520 case EVR_UT: // unlimited text
521 case EVR_PN: // person name
522 case EVR_UI: // unique identifier
523 case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR)
524 case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR
525 {
526 if (!(flags & DicomToJsonFlags_ConvertBinaryToNull))
527 {
528 Uint8* data = NULL;
529 if (element.getUint8Array(data) == EC_Normal)
530 {
531 return new DicomValue(reinterpret_cast<const char*>(data), element.getLength(), true);
532 }
533 }
534
535 return new DicomValue;
536 }
537
538 /**
539 * Numeric types
540 **/
541
542 case EVR_SL: // signed long
543 {
544 Sint32 f;
545 if (dynamic_cast<DcmSignedLong&>(element).getSint32(f).good())
546 return new DicomValue(boost::lexical_cast<std::string>(f), false);
547 else
548 return new DicomValue;
549 }
550
551 case EVR_SS: // signed short
552 {
553 Sint16 f;
554 if (dynamic_cast<DcmSignedShort&>(element).getSint16(f).good())
555 return new DicomValue(boost::lexical_cast<std::string>(f), false);
556 else
557 return new DicomValue;
558 }
559
560 case EVR_UL: // unsigned long
561 {
562 Uint32 f;
563 if (dynamic_cast<DcmUnsignedLong&>(element).getUint32(f).good())
564 return new DicomValue(boost::lexical_cast<std::string>(f), false);
565 else
566 return new DicomValue;
567 }
568
569 case EVR_US: // unsigned short
570 {
571 Uint16 f;
572 if (dynamic_cast<DcmUnsignedShort&>(element).getUint16(f).good())
573 return new DicomValue(boost::lexical_cast<std::string>(f), false);
574 else
575 return new DicomValue;
576 }
577
578 case EVR_FL: // float single-precision
579 {
580 Float32 f;
581 if (dynamic_cast<DcmFloatingPointSingle&>(element).getFloat32(f).good())
582 return new DicomValue(boost::lexical_cast<std::string>(f), false);
583 else
584 return new DicomValue;
585 }
586
587 case EVR_FD: // float double-precision
588 {
589 Float64 f;
590 if (dynamic_cast<DcmFloatingPointDouble&>(element).getFloat64(f).good())
591 return new DicomValue(boost::lexical_cast<std::string>(f), false);
592 else
593 return new DicomValue;
594 }
595
596
597 /**
598 * Attribute tag.
599 **/
600
601 case EVR_AT:
602 {
603 DcmTagKey tag;
604 if (dynamic_cast<DcmAttributeTag&>(element).getTagVal(tag, 0).good())
605 {
606 DicomTag t(tag.getGroup(), tag.getElement());
607 return new DicomValue(t.Format(), false);
608 }
609 else
610 {
611 return new DicomValue;
612 }
613 }
614
615
616 /**
617 * Sequence types, should never occur at this point because of
618 * "element.isLeaf()".
619 **/
620
621 case EVR_SQ: // sequence of items
622 return new DicomValue;
623
624
625 /**
626 * Internal to DCMTK.
627 **/
628
629 case EVR_xs: // SS or US depending on context
630 case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name)
631 case EVR_na: // na="not applicable", for data which has no VR
632 case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor
633 case EVR_item: // used internally for items
634 case EVR_metainfo: // used internally for meta info datasets
635 case EVR_dataset: // used internally for datasets
636 case EVR_fileFormat: // used internally for DICOM files
637 case EVR_dicomDir: // used internally for DICOMDIR objects
638 case EVR_dirRecord: // used internally for DICOMDIR records
639 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image
640 case EVR_pixelItem: // used internally for pixel items in a compressed image
641 case EVR_PixelData: // used internally for uncompressed pixeld data
642 case EVR_OverlayData: // used internally for overlay data
643 return new DicomValue;
644
645
646 /**
647 * Default case.
648 **/
649
650 default:
651 return new DicomValue;
652 }
653 }
654 catch (boost::bad_lexical_cast)
655 {
656 return new DicomValue;
657 }
658 catch (std::bad_cast)
659 {
660 return new DicomValue;
661 }
662 }
663
664
665 static Json::Value& PrepareNode(Json::Value& parent,
666 DcmElement& element,
667 DicomToJsonFormat format)
668 {
669 assert(parent.type() == Json::objectValue);
670
671 DicomTag tag(FromDcmtkBridge::GetTag(element));
672 const std::string formattedTag = tag.Format();
673
674 if (format == DicomToJsonFormat_Short)
675 {
676 parent[formattedTag] = Json::nullValue;
677 return parent[formattedTag];
678 }
679
680 // This code gives access to the name of the private tags
681 std::string tagName = FromDcmtkBridge::GetTagName(element);
682
683 switch (format)
684 {
685 case DicomToJsonFormat_Human:
686 parent[tagName] = Json::nullValue;
687 return parent[tagName];
688
689 case DicomToJsonFormat_Full:
690 {
691 parent[formattedTag] = Json::objectValue;
692 Json::Value& node = parent[formattedTag];
693
694 if (element.isLeaf())
695 {
696 node["Name"] = tagName;
697
698 if (element.getTag().getPrivateCreator() != NULL)
699 {
700 node["PrivateCreator"] = element.getTag().getPrivateCreator();
701 }
702
703 return node;
704 }
705 else
706 {
707 node["Name"] = tagName;
708 node["Type"] = "Sequence";
709 node["Value"] = Json::nullValue;
710 return node["Value"];
711 }
712 }
713
714 default:
715 throw OrthancException(ErrorCode_ParameterOutOfRange);
716 }
717 }
718
719
720 static void LeafValueToJson(Json::Value& target,
721 const DicomValue& value,
722 DicomToJsonFormat format,
723 DicomToJsonFlags flags,
724 unsigned int maxStringLength)
725 {
726 Json::Value* targetValue = NULL;
727 Json::Value* targetType = NULL;
728
729 switch (format)
730 {
731 case DicomToJsonFormat_Short:
732 case DicomToJsonFormat_Human:
733 {
734 assert(target.type() == Json::nullValue);
735 targetValue = &target;
736 break;
737 }
738
739 case DicomToJsonFormat_Full:
740 {
741 assert(target.type() == Json::objectValue);
742 target["Value"] = Json::nullValue;
743 target["Type"] = Json::nullValue;
744 targetType = &target["Type"];
745 targetValue = &target["Value"];
746 break;
747 }
748
749 default:
750 throw OrthancException(ErrorCode_ParameterOutOfRange);
751 }
752
753 assert(targetValue != NULL);
754 assert(targetValue->type() == Json::nullValue);
755 assert(targetType == NULL || targetType->type() == Json::nullValue);
756
757 if (value.IsNull())
758 {
759 if (targetType != NULL)
760 {
761 *targetType = "Null";
762 }
763 }
764 else if (value.IsBinary())
765 {
766 if (flags & DicomToJsonFlags_ConvertBinaryToAscii)
767 {
768 *targetValue = Toolbox::ConvertToAscii(value.GetContent());
769 }
770 else
771 {
772 std::string s;
773 value.FormatDataUriScheme(s);
774 *targetValue = s;
775 }
776
777 if (targetType != NULL)
778 {
779 *targetType = "Binary";
780 }
781 }
782 else if (maxStringLength == 0 ||
783 value.GetContent().size() <= maxStringLength)
784 {
785 *targetValue = value.GetContent();
786
787 if (targetType != NULL)
788 {
789 *targetType = "String";
790 }
791 }
792 else
793 {
794 if (targetType != NULL)
795 {
796 *targetType = "TooLong";
797 }
798 }
799 }
800
801
802 void FromDcmtkBridge::ElementToJson(Json::Value& parent,
803 DcmElement& element,
804 DicomToJsonFormat format,
805 DicomToJsonFlags flags,
806 unsigned int maxStringLength,
807 Encoding encoding)
808 {
809 if (parent.type() == Json::nullValue)
810 {
811 parent = Json::objectValue;
812 }
813
814 assert(parent.type() == Json::objectValue);
815 Json::Value& target = PrepareNode(parent, element, format);
816
817 if (element.isLeaf())
818 {
819 // The "0" below lets "LeafValueToJson()" take care of "TooLong" values
820 std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(element, flags, 0, encoding));
821 LeafValueToJson(target, *v, format, flags, maxStringLength);
822 }
823 else
824 {
825 assert(target.type() == Json::nullValue);
826 target = Json::arrayValue;
827
828 // "All subclasses of DcmElement except for DcmSequenceOfItems
829 // are leaf nodes, while DcmSequenceOfItems, DcmItem, DcmDataset
830 // etc. are not." The following dynamic_cast is thus OK.
831 DcmSequenceOfItems& sequence = dynamic_cast<DcmSequenceOfItems&>(element);
832
833 for (unsigned long i = 0; i < sequence.card(); i++)
834 {
835 DcmItem* child = sequence.getItem(i);
836 Json::Value& v = target.append(Json::objectValue);
837 DatasetToJson(v, *child, format, flags, maxStringLength, encoding);
838 }
839 }
840 }
841
842
843 void FromDcmtkBridge::DatasetToJson(Json::Value& parent,
844 DcmItem& item,
845 DicomToJsonFormat format,
846 DicomToJsonFlags flags,
847 unsigned int maxStringLength,
848 Encoding encoding)
849 {
850 assert(parent.type() == Json::objectValue);
851
852 for (unsigned long i = 0; i < item.card(); i++)
853 {
854 DcmElement* element = item.getElement(i);
855 if (element == NULL)
856 {
857 throw OrthancException(ErrorCode_InternalError);
858 }
859
860 DicomTag tag(FromDcmtkBridge::Convert(element->getTag()));
861
862 /*element->getTag().isPrivate()*/
863 if (tag.IsPrivate() &&
864 !(flags & DicomToJsonFlags_IncludePrivateTags))
865 {
866 continue;
867 }
868
869 if (!(flags & DicomToJsonFlags_IncludeUnknownTags))
870 {
871 DictionaryLocker locker;
872 if (locker->findEntry(element->getTag(), NULL) == NULL)
873 {
874 continue;
875 }
876 }
877
878 DcmEVR evr = element->getTag().getEVR();
879 if (evr == EVR_OB ||
880 evr == EVR_OF ||
881 evr == EVR_OW ||
882 evr == EVR_UN ||
883 evr == EVR_ox)
884 {
885 // This is a binary tag
886 if ((tag == DICOM_TAG_PIXEL_DATA && !(flags & DicomToJsonFlags_IncludePixelData)) ||
887 (tag != DICOM_TAG_PIXEL_DATA && !(flags & DicomToJsonFlags_IncludeBinary)))
888 {
889 continue;
890 }
891 }
892
893 FromDcmtkBridge::ElementToJson(parent, *element, format, flags, maxStringLength, encoding);
894 }
895 }
896
897
898 void FromDcmtkBridge::ExtractDicomAsJson(Json::Value& target,
899 DcmDataset& dataset,
900 DicomToJsonFormat format,
901 DicomToJsonFlags flags,
902 unsigned int maxStringLength,
903 Encoding defaultEncoding)
904 {
905 Encoding encoding = DetectEncoding(dataset, defaultEncoding);
906
907 target = Json::objectValue;
908 DatasetToJson(target, dataset, format, flags, maxStringLength, encoding);
909 }
910
911
912 void FromDcmtkBridge::ExtractHeaderAsJson(Json::Value& target,
913 DcmMetaInfo& dataset,
914 DicomToJsonFormat format,
915 DicomToJsonFlags flags,
916 unsigned int maxStringLength)
917 {
918 target = Json::objectValue;
919 DatasetToJson(target, dataset, format, flags, maxStringLength, Encoding_Ascii);
920 }
921
922
923
924 static std::string GetTagNameInternal(DcmTag& tag)
925 {
926 {
927 // Some patches for important tags because of different DICOM
928 // dictionaries between DCMTK versions
929 DicomTag tmp(tag.getGroup(), tag.getElement());
930 std::string n = tmp.GetMainTagsName();
931 if (n.size() != 0)
932 {
933 return n;
934 }
935 // End of patches
936 }
937
938 #if 0
939 // This version explicitly calls the dictionary
940 const DcmDataDictionary& dict = dcmDataDict.rdlock();
941 const DcmDictEntry* entry = dict.findEntry(tag, NULL);
942
943 std::string s(DcmTag_ERROR_TagName);
944 if (entry != NULL)
945 {
946 s = std::string(entry->getTagName());
947 }
948
949 dcmDataDict.unlock();
950 return s;
951 #else
952 const char* name = tag.getTagName();
953 if (name == NULL)
954 {
955 return DcmTag_ERROR_TagName;
956 }
957 else
958 {
959 return std::string(name);
960 }
961 #endif
962 }
963
964
965 std::string FromDcmtkBridge::GetTagName(const DicomTag& t,
966 const std::string& privateCreator)
967 {
968 DcmTag tag(t.GetGroup(), t.GetElement());
969
970 if (!privateCreator.empty())
971 {
972 tag.setPrivateCreator(privateCreator.c_str());
973 }
974
975 return GetTagNameInternal(tag);
976 }
977
978
979 std::string FromDcmtkBridge::GetTagName(const DcmElement& element)
980 {
981 // Copy the tag to ensure const-correctness of DcmElement. Note
982 // that the private creator information is also copied.
983 DcmTag tag(element.getTag());
984
985 return GetTagNameInternal(tag);
986 }
987
988
989
990 DicomTag FromDcmtkBridge::ParseTag(const char* name)
991 {
992 if (strlen(name) == 9 &&
993 isxdigit(name[0]) &&
994 isxdigit(name[1]) &&
995 isxdigit(name[2]) &&
996 isxdigit(name[3]) &&
997 (name[4] == '-' || name[4] == ',') &&
998 isxdigit(name[5]) &&
999 isxdigit(name[6]) &&
1000 isxdigit(name[7]) &&
1001 isxdigit(name[8]))
1002 {
1003 uint16_t group = GetTagValue(name);
1004 uint16_t element = GetTagValue(name + 5);
1005 return DicomTag(group, element);
1006 }
1007
1008 if (strlen(name) == 8 &&
1009 isxdigit(name[0]) &&
1010 isxdigit(name[1]) &&
1011 isxdigit(name[2]) &&
1012 isxdigit(name[3]) &&
1013 isxdigit(name[4]) &&
1014 isxdigit(name[5]) &&
1015 isxdigit(name[6]) &&
1016 isxdigit(name[7]))
1017 {
1018 uint16_t group = GetTagValue(name);
1019 uint16_t element = GetTagValue(name + 4);
1020 return DicomTag(group, element);
1021 }
1022
1023 #if 0
1024 const DcmDataDictionary& dict = dcmDataDict.rdlock();
1025 const DcmDictEntry* entry = dict.findEntry(name);
1026
1027 if (entry == NULL)
1028 {
1029 dcmDataDict.unlock();
1030 throw OrthancException(ErrorCode_UnknownDicomTag);
1031 }
1032 else
1033 {
1034 DcmTagKey key = entry->getKey();
1035 DicomTag tag(key.getGroup(), key.getElement());
1036 dcmDataDict.unlock();
1037 return tag;
1038 }
1039 #else
1040 DcmTag tag;
1041 if (DcmTag::findTagFromName(name, tag).good())
1042 {
1043 return DicomTag(tag.getGTag(), tag.getETag());
1044 }
1045 else
1046 {
1047 throw OrthancException(ErrorCode_UnknownDicomTag);
1048 }
1049 #endif
1050 }
1051
1052
1053 bool FromDcmtkBridge::IsUnknownTag(const DicomTag& tag)
1054 {
1055 DcmTag tmp(tag.GetGroup(), tag.GetElement());
1056 return tmp.isUnknownVR();
1057 }
1058
1059
1060 void FromDcmtkBridge::ToJson(Json::Value& result,
1061 const DicomMap& values,
1062 bool simplify)
1063 {
1064 if (result.type() != Json::objectValue)
1065 {
1066 throw OrthancException(ErrorCode_BadParameterType);
1067 }
1068
1069 result.clear();
1070
1071 for (DicomMap::Map::const_iterator
1072 it = values.map_.begin(); it != values.map_.end(); ++it)
1073 {
1074 // TODO Inject PrivateCreator if some is available in the DicomMap?
1075 const std::string tagName = GetTagName(it->first, "");
1076
1077 if (simplify)
1078 {
1079 if (it->second->IsNull())
1080 {
1081 result[tagName] = Json::nullValue;
1082 }
1083 else
1084 {
1085 // TODO IsBinary
1086 result[tagName] = it->second->GetContent();
1087 }
1088 }
1089 else
1090 {
1091 Json::Value value = Json::objectValue;
1092
1093 value["Name"] = tagName;
1094
1095 if (it->second->IsNull())
1096 {
1097 value["Type"] = "Null";
1098 value["Value"] = Json::nullValue;
1099 }
1100 else
1101 {
1102 // TODO IsBinary
1103 value["Type"] = "String";
1104 value["Value"] = it->second->GetContent();
1105 }
1106
1107 result[it->first.Format()] = value;
1108 }
1109 }
1110 }
1111
1112
1113 std::string FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType level)
1114 {
1115 char uid[100];
1116
1117 switch (level)
1118 {
1119 case ResourceType_Patient:
1120 // The "PatientID" field is of type LO (Long String), 64
1121 // Bytes Maximum. An UUID is of length 36, thus it can be used
1122 // as a random PatientID.
1123 return SystemToolbox::GenerateUuid();
1124
1125 case ResourceType_Instance:
1126 return dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT);
1127
1128 case ResourceType_Series:
1129 return dcmGenerateUniqueIdentifier(uid, SITE_SERIES_UID_ROOT);
1130
1131 case ResourceType_Study:
1132 return dcmGenerateUniqueIdentifier(uid, SITE_STUDY_UID_ROOT);
1133
1134 default:
1135 throw OrthancException(ErrorCode_ParameterOutOfRange);
1136 }
1137 }
1138
1139 bool FromDcmtkBridge::SaveToMemoryBuffer(std::string& buffer,
1140 DcmDataset& dataSet)
1141 {
1142 // Determine the transfer syntax which shall be used to write the
1143 // information to the file. We always switch to the Little Endian
1144 // syntax, with explicit length.
1145
1146 // http://support.dcmtk.org/docs/dcxfer_8h-source.html
1147
1148
1149 /**
1150 * Note that up to Orthanc 0.7.1 (inclusive), the
1151 * "EXS_LittleEndianExplicit" was always used to save the DICOM
1152 * dataset into memory. We now keep the original transfer syntax
1153 * (if available).
1154 **/
1155 E_TransferSyntax xfer = dataSet.getOriginalXfer();
1156 if (xfer == EXS_Unknown)
1157 {
1158 // No information about the original transfer syntax: This is
1159 // most probably a DICOM dataset that was read from memory.
1160 xfer = EXS_LittleEndianExplicit;
1161 }
1162
1163 E_EncodingType encodingType = /*opt_sequenceType*/ EET_ExplicitLength;
1164
1165 // Create the meta-header information
1166 DcmFileFormat ff(&dataSet);
1167 ff.validateMetaInfo(xfer);
1168 ff.removeInvalidGroups();
1169
1170 // Create a memory buffer with the proper size
1171 {
1172 const uint32_t estimatedSize = ff.calcElementLength(xfer, encodingType); // (*)
1173 buffer.resize(estimatedSize);
1174 }
1175
1176 DcmOutputBufferStream ob(&buffer[0], buffer.size());
1177
1178 // Fill the memory buffer with the meta-header and the dataset
1179 ff.transferInit();
1180 OFCondition c = ff.write(ob, xfer, encodingType, NULL,
1181 /*opt_groupLength*/ EGL_recalcGL,
1182 /*opt_paddingType*/ EPD_withoutPadding);
1183 ff.transferEnd();
1184
1185 if (c.good())
1186 {
1187 // The DICOM file is successfully written, truncate the target
1188 // buffer if its size was overestimated by (*)
1189 ob.flush();
1190
1191 size_t effectiveSize = static_cast<size_t>(ob.tell());
1192 if (effectiveSize < buffer.size())
1193 {
1194 buffer.resize(effectiveSize);
1195 }
1196
1197 return true;
1198 }
1199 else
1200 {
1201 // Error
1202 buffer.clear();
1203 return false;
1204 }
1205 }
1206
1207
1208 ValueRepresentation FromDcmtkBridge::LookupValueRepresentation(const DicomTag& tag)
1209 {
1210 DcmTag t(tag.GetGroup(), tag.GetElement());
1211 return Convert(t.getEVR());
1212 }
1213
1214 ValueRepresentation FromDcmtkBridge::Convert(const DcmEVR vr)
1215 {
1216 switch (vr)
1217 {
1218 case EVR_AE:
1219 return ValueRepresentation_ApplicationEntity;
1220
1221 case EVR_AS:
1222 return ValueRepresentation_AgeString;
1223
1224 case EVR_AT:
1225 return ValueRepresentation_AttributeTag;
1226
1227 case EVR_CS:
1228 return ValueRepresentation_CodeString;
1229
1230 case EVR_DA:
1231 return ValueRepresentation_Date;
1232
1233 case EVR_DS:
1234 return ValueRepresentation_DecimalString;
1235
1236 case EVR_DT:
1237 return ValueRepresentation_DateTime;
1238
1239 case EVR_FL:
1240 return ValueRepresentation_FloatingPointSingle;
1241
1242 case EVR_FD:
1243 return ValueRepresentation_FloatingPointDouble;
1244
1245 case EVR_IS:
1246 return ValueRepresentation_IntegerString;
1247
1248 case EVR_LO:
1249 return ValueRepresentation_LongString;
1250
1251 case EVR_LT:
1252 return ValueRepresentation_LongText;
1253
1254 case EVR_OB:
1255 return ValueRepresentation_OtherByte;
1256
1257 // Not supported as of DCMTK 3.6.0
1258 /*case EVR_OD:
1259 return ValueRepresentation_OtherDouble;*/
1260
1261 case EVR_OF:
1262 return ValueRepresentation_OtherFloat;
1263
1264 // Not supported as of DCMTK 3.6.0
1265 /*case EVR_OL:
1266 return ValueRepresentation_OtherLong;*/
1267
1268 case EVR_OW:
1269 return ValueRepresentation_OtherWord;
1270
1271 case EVR_PN:
1272 return ValueRepresentation_PersonName;
1273
1274 case EVR_SH:
1275 return ValueRepresentation_ShortString;
1276
1277 case EVR_SL:
1278 return ValueRepresentation_SignedLong;
1279
1280 case EVR_SQ:
1281 return ValueRepresentation_Sequence;
1282
1283 case EVR_SS:
1284 return ValueRepresentation_SignedShort;
1285
1286 case EVR_ST:
1287 return ValueRepresentation_ShortText;
1288
1289 case EVR_TM:
1290 return ValueRepresentation_Time;
1291
1292 // Not supported as of DCMTK 3.6.0
1293 /*case EVR_UC:
1294 return ValueRepresentation_UnlimitedCharacters;*/
1295
1296 case EVR_UI:
1297 return ValueRepresentation_UniqueIdentifier;
1298
1299 case EVR_UL:
1300 return ValueRepresentation_UnsignedLong;
1301
1302 case EVR_UN:
1303 return ValueRepresentation_Unknown;
1304
1305 // Not supported as of DCMTK 3.6.0
1306 /*case EVR_UR:
1307 return ValueRepresentation_UniversalResource;*/
1308
1309 case EVR_US:
1310 return ValueRepresentation_UnsignedShort;
1311
1312 case EVR_UT:
1313 return ValueRepresentation_UnlimitedText;
1314
1315 default:
1316 return ValueRepresentation_NotSupported;
1317 }
1318 }
1319
1320
1321 static bool IsBinaryTag(const DcmTag& key)
1322 {
1323 return (key.isUnknownVR() ||
1324 key.getEVR() == EVR_OB ||
1325 key.getEVR() == EVR_OF ||
1326 key.getEVR() == EVR_OW ||
1327 key.getEVR() == EVR_UN ||
1328 key.getEVR() == EVR_ox);
1329 }
1330
1331
1332 DcmElement* FromDcmtkBridge::CreateElementForTag(const DicomTag& tag)
1333 {
1334 DcmTag key(tag.GetGroup(), tag.GetElement());
1335
1336 if (tag.IsPrivate() ||
1337 IsBinaryTag(key))
1338 {
1339 return new DcmOtherByteOtherWord(key);
1340 }
1341
1342 switch (key.getEVR())
1343 {
1344 // http://support.dcmtk.org/docs/dcvr_8h-source.html
1345
1346 /**
1347 * Binary types, handled above
1348 **/
1349
1350 case EVR_OB: // other byte
1351 case EVR_OF: // other float
1352 case EVR_OW: // other word
1353 case EVR_UN: // unknown value representation
1354 case EVR_ox: // OB or OW depending on context
1355 throw OrthancException(ErrorCode_InternalError);
1356
1357
1358 /**
1359 * String types.
1360 * http://support.dcmtk.org/docs/classDcmByteString.html
1361 **/
1362
1363 case EVR_AS: // age string
1364 return new DcmAgeString(key);
1365
1366 case EVR_AE: // application entity title
1367 return new DcmApplicationEntity(key);
1368
1369 case EVR_CS: // code string
1370 return new DcmCodeString(key);
1371
1372 case EVR_DA: // date string
1373 return new DcmDate(key);
1374
1375 case EVR_DT: // date time string
1376 return new DcmDateTime(key);
1377
1378 case EVR_DS: // decimal string
1379 return new DcmDecimalString(key);
1380
1381 case EVR_IS: // integer string
1382 return new DcmIntegerString(key);
1383
1384 case EVR_TM: // time string
1385 return new DcmTime(key);
1386
1387 case EVR_UI: // unique identifier
1388 return new DcmUniqueIdentifier(key);
1389
1390 case EVR_ST: // short text
1391 return new DcmShortText(key);
1392
1393 case EVR_LO: // long string
1394 return new DcmLongString(key);
1395
1396 case EVR_LT: // long text
1397 return new DcmLongText(key);
1398
1399 case EVR_UT: // unlimited text
1400 return new DcmUnlimitedText(key);
1401
1402 case EVR_SH: // short string
1403 return new DcmShortString(key);
1404
1405 case EVR_PN: // person name
1406 return new DcmPersonName(key);
1407
1408
1409 /**
1410 * Numerical types
1411 **/
1412
1413 case EVR_SL: // signed long
1414 return new DcmSignedLong(key);
1415
1416 case EVR_SS: // signed short
1417 return new DcmSignedShort(key);
1418
1419 case EVR_UL: // unsigned long
1420 return new DcmUnsignedLong(key);
1421
1422 case EVR_US: // unsigned short
1423 return new DcmUnsignedShort(key);
1424
1425 case EVR_FL: // float single-precision
1426 return new DcmFloatingPointSingle(key);
1427
1428 case EVR_FD: // float double-precision
1429 return new DcmFloatingPointDouble(key);
1430
1431
1432 /**
1433 * Sequence types, should never occur at this point.
1434 **/
1435
1436 case EVR_SQ: // sequence of items
1437 throw OrthancException(ErrorCode_ParameterOutOfRange);
1438
1439
1440 /**
1441 * TODO
1442 **/
1443
1444 case EVR_AT: // attribute tag
1445 throw OrthancException(ErrorCode_NotImplemented);
1446
1447
1448 /**
1449 * Internal to DCMTK.
1450 **/
1451
1452 case EVR_xs: // SS or US depending on context
1453 case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name)
1454 case EVR_na: // na="not applicable", for data which has no VR
1455 case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor
1456 case EVR_item: // used internally for items
1457 case EVR_metainfo: // used internally for meta info datasets
1458 case EVR_dataset: // used internally for datasets
1459 case EVR_fileFormat: // used internally for DICOM files
1460 case EVR_dicomDir: // used internally for DICOMDIR objects
1461 case EVR_dirRecord: // used internally for DICOMDIR records
1462 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image
1463 case EVR_pixelItem: // used internally for pixel items in a compressed image
1464 case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR)
1465 case EVR_PixelData: // used internally for uncompressed pixeld data
1466 case EVR_OverlayData: // used internally for overlay data
1467 case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR
1468 default:
1469 break;
1470 }
1471
1472 throw OrthancException(ErrorCode_InternalError);
1473 }
1474
1475
1476
1477 void FromDcmtkBridge::FillElementWithString(DcmElement& element,
1478 const DicomTag& tag,
1479 const std::string& utf8Value,
1480 bool decodeDataUriScheme,
1481 Encoding dicomEncoding)
1482 {
1483 std::string binary;
1484 const std::string* decoded = &utf8Value;
1485
1486 if (decodeDataUriScheme &&
1487 boost::starts_with(utf8Value, "data:application/octet-stream;base64,"))
1488 {
1489 std::string mime;
1490 if (!Toolbox::DecodeDataUriScheme(mime, binary, utf8Value))
1491 {
1492 throw OrthancException(ErrorCode_BadFileFormat);
1493 }
1494
1495 decoded = &binary;
1496 }
1497 else if (dicomEncoding != Encoding_Utf8)
1498 {
1499 binary = Toolbox::ConvertFromUtf8(utf8Value, dicomEncoding);
1500 decoded = &binary;
1501 }
1502
1503 DcmTag key(tag.GetGroup(), tag.GetElement());
1504
1505 if (tag.IsPrivate() ||
1506 IsBinaryTag(key))
1507 {
1508 if (element.putUint8Array((const Uint8*) decoded->c_str(), decoded->size()).good())
1509 {
1510 return;
1511 }
1512 else
1513 {
1514 throw OrthancException(ErrorCode_InternalError);
1515 }
1516 }
1517
1518 bool ok = false;
1519
1520 try
1521 {
1522 switch (key.getEVR())
1523 {
1524 // http://support.dcmtk.org/docs/dcvr_8h-source.html
1525
1526 /**
1527 * TODO.
1528 **/
1529
1530 case EVR_OB: // other byte
1531 case EVR_OF: // other float
1532 case EVR_OW: // other word
1533 case EVR_AT: // attribute tag
1534 throw OrthancException(ErrorCode_NotImplemented);
1535
1536 case EVR_UN: // unknown value representation
1537 throw OrthancException(ErrorCode_ParameterOutOfRange);
1538
1539
1540 /**
1541 * String types.
1542 **/
1543
1544 case EVR_DS: // decimal string
1545 case EVR_IS: // integer string
1546 case EVR_AS: // age string
1547 case EVR_DA: // date string
1548 case EVR_DT: // date time string
1549 case EVR_TM: // time string
1550 case EVR_AE: // application entity title
1551 case EVR_CS: // code string
1552 case EVR_SH: // short string
1553 case EVR_LO: // long string
1554 case EVR_ST: // short text
1555 case EVR_LT: // long text
1556 case EVR_UT: // unlimited text
1557 case EVR_PN: // person name
1558 case EVR_UI: // unique identifier
1559 {
1560 ok = element.putString(decoded->c_str()).good();
1561 break;
1562 }
1563
1564
1565 /**
1566 * Numerical types
1567 **/
1568
1569 case EVR_SL: // signed long
1570 {
1571 ok = element.putSint32(boost::lexical_cast<Sint32>(*decoded)).good();
1572 break;
1573 }
1574
1575 case EVR_SS: // signed short
1576 {
1577 ok = element.putSint16(boost::lexical_cast<Sint16>(*decoded)).good();
1578 break;
1579 }
1580
1581 case EVR_UL: // unsigned long
1582 {
1583 ok = element.putUint32(boost::lexical_cast<Uint32>(*decoded)).good();
1584 break;
1585 }
1586
1587 case EVR_US: // unsigned short
1588 {
1589 ok = element.putUint16(boost::lexical_cast<Uint16>(*decoded)).good();
1590 break;
1591 }
1592
1593 case EVR_FL: // float single-precision
1594 {
1595 ok = element.putFloat32(boost::lexical_cast<float>(*decoded)).good();
1596 break;
1597 }
1598
1599 case EVR_FD: // float double-precision
1600 {
1601 ok = element.putFloat64(boost::lexical_cast<double>(*decoded)).good();
1602 break;
1603 }
1604
1605
1606 /**
1607 * Sequence types, should never occur at this point.
1608 **/
1609
1610 case EVR_SQ: // sequence of items
1611 {
1612 ok = false;
1613 break;
1614 }
1615
1616
1617 /**
1618 * Internal to DCMTK.
1619 **/
1620
1621 case EVR_ox: // OB or OW depending on context
1622 case EVR_xs: // SS or US depending on context
1623 case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name)
1624 case EVR_na: // na="not applicable", for data which has no VR
1625 case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor
1626 case EVR_item: // used internally for items
1627 case EVR_metainfo: // used internally for meta info datasets
1628 case EVR_dataset: // used internally for datasets
1629 case EVR_fileFormat: // used internally for DICOM files
1630 case EVR_dicomDir: // used internally for DICOMDIR objects
1631 case EVR_dirRecord: // used internally for DICOMDIR records
1632 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image
1633 case EVR_pixelItem: // used internally for pixel items in a compressed image
1634 case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR)
1635 case EVR_PixelData: // used internally for uncompressed pixeld data
1636 case EVR_OverlayData: // used internally for overlay data
1637 case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR
1638 default:
1639 break;
1640 }
1641 }
1642 catch (boost::bad_lexical_cast&)
1643 {
1644 ok = false;
1645 }
1646
1647 if (!ok)
1648 {
1649 throw OrthancException(ErrorCode_InternalError);
1650 }
1651 }
1652
1653
1654 DcmElement* FromDcmtkBridge::FromJson(const DicomTag& tag,
1655 const Json::Value& value,
1656 bool decodeDataUriScheme,
1657 Encoding dicomEncoding)
1658 {
1659 std::auto_ptr<DcmElement> element;
1660
1661 switch (value.type())
1662 {
1663 case Json::stringValue:
1664 element.reset(CreateElementForTag(tag));
1665 FillElementWithString(*element, tag, value.asString(), decodeDataUriScheme, dicomEncoding);
1666 break;
1667
1668 case Json::arrayValue:
1669 {
1670 DcmTag key(tag.GetGroup(), tag.GetElement());
1671 if (key.getEVR() != EVR_SQ)
1672 {
1673 throw OrthancException(ErrorCode_BadParameterType);
1674 }
1675
1676 DcmSequenceOfItems* sequence = new DcmSequenceOfItems(key, value.size());
1677 element.reset(sequence);
1678
1679 for (Json::Value::ArrayIndex i = 0; i < value.size(); i++)
1680 {
1681 std::auto_ptr<DcmItem> item(new DcmItem);
1682
1683 Json::Value::Members members = value[i].getMemberNames();
1684 for (Json::Value::ArrayIndex j = 0; j < members.size(); j++)
1685 {
1686 item->insert(FromJson(ParseTag(members[j]), value[i][members[j]], decodeDataUriScheme, dicomEncoding));
1687 }
1688
1689 sequence->append(item.release());
1690 }
1691
1692 break;
1693 }
1694
1695 default:
1696 throw OrthancException(ErrorCode_BadParameterType);
1697 }
1698
1699 return element.release();
1700 }
1701
1702
1703 DcmPixelSequence* FromDcmtkBridge::GetPixelSequence(DcmDataset& dataset)
1704 {
1705 DcmElement *element = NULL;
1706 if (!dataset.findAndGetElement(DCM_PixelData, element).good())
1707 {
1708 throw OrthancException(ErrorCode_BadFileFormat);
1709 }
1710
1711 DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element);
1712 DcmPixelSequence* pixelSequence = NULL;
1713 if (!pixelData.getEncapsulatedRepresentation
1714 (dataset.getOriginalXfer(), NULL, pixelSequence).good())
1715 {
1716 return NULL;
1717 }
1718 else
1719 {
1720 return pixelSequence;
1721 }
1722 }
1723
1724
1725 Encoding FromDcmtkBridge::ExtractEncoding(const Json::Value& json,
1726 Encoding defaultEncoding)
1727 {
1728 if (json.type() != Json::objectValue)
1729 {
1730 throw OrthancException(ErrorCode_BadParameterType);
1731 }
1732
1733 Encoding encoding = defaultEncoding;
1734
1735 const Json::Value::Members tags = json.getMemberNames();
1736
1737 // Look for SpecificCharacterSet (0008,0005) in the JSON file
1738 for (size_t i = 0; i < tags.size(); i++)
1739 {
1740 DicomTag tag = FromDcmtkBridge::ParseTag(tags[i]);
1741 if (tag == DICOM_TAG_SPECIFIC_CHARACTER_SET)
1742 {
1743 const Json::Value& value = json[tags[i]];
1744 if (value.type() != Json::stringValue ||
1745 !GetDicomEncoding(encoding, value.asCString()))
1746 {
1747 LOG(ERROR) << "Unknown encoding while creating DICOM from JSON: " << value;
1748 throw OrthancException(ErrorCode_BadRequest);
1749 }
1750 }
1751 }
1752
1753 return encoding;
1754 }
1755
1756
1757 static void SetString(DcmDataset& target,
1758 const DcmTag& tag,
1759 const std::string& value)
1760 {
1761 if (!target.putAndInsertString(tag, value.c_str()).good())
1762 {
1763 throw OrthancException(ErrorCode_InternalError);
1764 }
1765 }
1766
1767
1768 DcmDataset* FromDcmtkBridge::FromJson(const Json::Value& json, // Encoded using UTF-8
1769 bool generateIdentifiers,
1770 bool decodeDataUriScheme,
1771 Encoding defaultEncoding)
1772 {
1773 std::auto_ptr<DcmDataset> result(new DcmDataset);
1774 Encoding encoding = ExtractEncoding(json, defaultEncoding);
1775
1776 SetString(*result, DCM_SpecificCharacterSet, GetDicomSpecificCharacterSet(encoding));
1777
1778 const Json::Value::Members tags = json.getMemberNames();
1779
1780 bool hasPatientId = false;
1781 bool hasStudyInstanceUid = false;
1782 bool hasSeriesInstanceUid = false;
1783 bool hasSopInstanceUid = false;
1784
1785 for (size_t i = 0; i < tags.size(); i++)
1786 {
1787 DicomTag tag = FromDcmtkBridge::ParseTag(tags[i]);
1788 const Json::Value& value = json[tags[i]];
1789
1790 if (tag == DICOM_TAG_PATIENT_ID)
1791 {
1792 hasPatientId = true;
1793 }
1794 else if (tag == DICOM_TAG_STUDY_INSTANCE_UID)
1795 {
1796 hasStudyInstanceUid = true;
1797 }
1798 else if (tag == DICOM_TAG_SERIES_INSTANCE_UID)
1799 {
1800 hasSeriesInstanceUid = true;
1801 }
1802 else if (tag == DICOM_TAG_SOP_INSTANCE_UID)
1803 {
1804 hasSopInstanceUid = true;
1805 }
1806
1807 if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET)
1808 {
1809 std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, encoding));
1810 const DcmTagKey& tag = element->getTag();
1811
1812 result->findAndDeleteElement(tag);
1813
1814 DcmElement* tmp = element.release();
1815 if (!result->insert(tmp, false, false).good())
1816 {
1817 delete tmp;
1818 throw OrthancException(ErrorCode_InternalError);
1819 }
1820 }
1821 }
1822
1823 if (!hasPatientId &&
1824 generateIdentifiers)
1825 {
1826 SetString(*result, DCM_PatientID, GenerateUniqueIdentifier(ResourceType_Patient));
1827 }
1828
1829 if (!hasStudyInstanceUid &&
1830 generateIdentifiers)
1831 {
1832 SetString(*result, DCM_StudyInstanceUID, GenerateUniqueIdentifier(ResourceType_Study));
1833 }
1834
1835 if (!hasSeriesInstanceUid &&
1836 generateIdentifiers)
1837 {
1838 SetString(*result, DCM_SeriesInstanceUID, GenerateUniqueIdentifier(ResourceType_Series));
1839 }
1840
1841 if (!hasSopInstanceUid &&
1842 generateIdentifiers)
1843 {
1844 SetString(*result, DCM_SOPInstanceUID, GenerateUniqueIdentifier(ResourceType_Instance));
1845 }
1846
1847 return result.release();
1848 }
1849
1850
1851 DcmFileFormat* FromDcmtkBridge::LoadFromMemoryBuffer(const void* buffer,
1852 size_t size)
1853 {
1854 DcmInputBufferStream is;
1855 if (size > 0)
1856 {
1857 is.setBuffer(buffer, size);
1858 }
1859 is.setEos();
1860
1861 std::auto_ptr<DcmFileFormat> result(new DcmFileFormat);
1862
1863 result->transferInit();
1864 if (!result->read(is).good())
1865 {
1866 throw OrthancException(ErrorCode_BadFileFormat);
1867 }
1868
1869 result->loadAllDataIntoMemory();
1870 result->transferEnd();
1871
1872 return result.release();
1873 }
1874
1875
1876 void FromDcmtkBridge::FromJson(DicomMap& target,
1877 const Json::Value& source)
1878 {
1879 if (source.type() != Json::objectValue)
1880 {
1881 throw OrthancException(ErrorCode_BadFileFormat);
1882 }
1883
1884 target.Clear();
1885
1886 Json::Value::Members members = source.getMemberNames();
1887
1888 for (size_t i = 0; i < members.size(); i++)
1889 {
1890 const Json::Value& value = source[members[i]];
1891
1892 if (value.type() != Json::stringValue)
1893 {
1894 throw OrthancException(ErrorCode_BadFileFormat);
1895 }
1896
1897 target.SetValue(ParseTag(members[i]), value.asString(), false);
1898 }
1899 }
1900 }