comparison Core/DicomParsing/FromDcmtkBridge.cpp @ 3691:4922bdd046dd

Fix issue #140 (Modifying private tags with REST API changes VR from LO to UN) - DANGEROUS COMMIT
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 25 Feb 2020 21:44:09 +0100
parents 46cb00e4adbb
children fd302ec6a502
comparison
equal deleted inserted replaced
3690:a9ce35d67c3c 3691:4922bdd046dd
113 #endif 113 #endif
114 114
115 115
116 namespace Orthanc 116 namespace Orthanc
117 { 117 {
118 static bool IsBinaryTag(const DcmTag& key)
119 {
120 return (key.isUnknownVR() ||
121 key.getEVR() == EVR_OB ||
122 key.getEVR() == EVR_OW ||
123 key.getEVR() == EVR_UN ||
124 key.getEVR() == EVR_ox);
125 }
126
127
118 #if DCMTK_USE_EMBEDDED_DICTIONARIES == 1 128 #if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
119 static void LoadEmbeddedDictionary(DcmDataDictionary& dictionary, 129 static void LoadEmbeddedDictionary(DcmDataDictionary& dictionary,
120 EmbeddedResources::FileResourceId resource) 130 EmbeddedResources::FileResourceId resource)
121 { 131 {
122 std::string content; 132 std::string content;
936 } 946 }
937 947
938 if (!(flags & DicomToJsonFlags_IncludeUnknownTags)) 948 if (!(flags & DicomToJsonFlags_IncludeUnknownTags))
939 { 949 {
940 DictionaryLocker locker; 950 DictionaryLocker locker;
941 if (locker->findEntry(element->getTag(), NULL) == NULL) 951 if (locker->findEntry(element->getTag(), element->getTag().getPrivateCreator()) == NULL)
942 { 952 {
943 continue; 953 continue;
944 } 954 }
945 } 955 }
946 956
947 DcmEVR evr = element->getTag().getEVR(); 957 if (IsBinaryTag(element->getTag()))
948 if (evr == EVR_OB ||
949 evr == EVR_OF ||
950 evr == EVR_OW ||
951 evr == EVR_UN ||
952 evr == EVR_ox)
953 { 958 {
954 // This is a binary tag 959 // This is a binary tag
955 if ((tag == DICOM_TAG_PIXEL_DATA && !(flags & DicomToJsonFlags_IncludePixelData)) || 960 if ((tag == DICOM_TAG_PIXEL_DATA && !(flags & DicomToJsonFlags_IncludePixelData)) ||
956 (tag != DICOM_TAG_PIXEL_DATA && !(flags & DicomToJsonFlags_IncludeBinary))) 961 (tag != DICOM_TAG_PIXEL_DATA && !(flags & DicomToJsonFlags_IncludeBinary)))
957 { 962 {
1369 return ValueRepresentation_NotSupported; 1374 return ValueRepresentation_NotSupported;
1370 } 1375 }
1371 } 1376 }
1372 1377
1373 1378
1374 static bool IsBinaryTag(const DcmTag& key) 1379 DcmElement* FromDcmtkBridge::CreateElementForTag(const DicomTag& tag,
1375 { 1380 const std::string& privateCreator)
1376 return (key.isUnknownVR() || 1381 {
1377 #if DCMTK_VERSION_NUMBER >= 361 1382 if (tag.IsPrivate() &&
1378 key.getEVR() == EVR_OD || 1383 privateCreator.empty())
1379 #endif 1384 {
1380 1385 // This solves issue 140 (Modifying private tags with REST API
1381 #if DCMTK_VERSION_NUMBER >= 362 1386 // changes VR from LO to UN)
1382 key.getEVR() == EVR_OL || 1387 // https://bitbucket.org/sjodogne/orthanc/issues/140
1383 #endif 1388 LOG(WARNING) << "Private creator should not be empty while creating a private tag: " << tag.Format();
1384 key.getEVR() == EVR_OB || 1389 }
1385 key.getEVR() == EVR_OF ||
1386 key.getEVR() == EVR_OW ||
1387 key.getEVR() == EVR_UN ||
1388 key.getEVR() == EVR_ox);
1389 }
1390
1391
1392 DcmElement* FromDcmtkBridge::CreateElementForTag(const DicomTag& tag)
1393 {
1394 DcmTag key(tag.GetGroup(), tag.GetElement());
1395
1396 if (tag.IsPrivate() ||
1397 IsBinaryTag(key))
1398 {
1399 return new DcmOtherByteOtherWord(key);
1400 }
1401
1402 switch (key.getEVR())
1403 {
1404 // http://support.dcmtk.org/docs/dcvr_8h-source.html
1405
1406 /**
1407 * Binary types, handled above
1408 **/
1409 1390
1410 #if DCMTK_VERSION_NUMBER >= 361 1391 #if DCMTK_VERSION_NUMBER >= 361
1411 case EVR_OD: 1392 DcmTag key(tag.GetGroup(), tag.GetElement());
1412 #endif 1393 if (tag.IsPrivate())
1413 1394 {
1414 #if DCMTK_VERSION_NUMBER >= 362 1395 return DcmItem::newDicomElement(key, privateCreator.c_str());
1415 case EVR_OL: 1396 }
1416 #endif 1397 else
1417 1398 {
1418 case EVR_OB: // other byte 1399 return DcmItem::newDicomElement(key, NULL);
1419 case EVR_OF: // other float 1400 }
1420 case EVR_OW: // other word 1401
1421 case EVR_UN: // unknown value representation 1402 #else
1422 case EVR_ox: // OB or OW depending on context 1403 DcmTag key(tag.GetGroup(), tag.GetElement());
1423 throw OrthancException(ErrorCode_InternalError); 1404 if (tag.IsPrivate())
1424 1405 {
1425 1406 // https://forum.dcmtk.org/viewtopic.php?t=4527
1426 /** 1407 LOG(WARNING) << "You are using DCMTK <= 3.6.0: All the private tags "
1427 * String types. 1408 "are considered as having a binary value representation";
1428 * http://support.dcmtk.org/docs/classDcmByteString.html 1409 key.setPrivateCreator(privateCreator.c_str());
1429 **/ 1410 return new DcmOtherByteOtherWord(key);
1430 1411 }
1431 case EVR_AS: // age string 1412 else
1432 return new DcmAgeString(key); 1413 {
1433 1414 return newDicomElement(key);
1434 case EVR_AE: // application entity title 1415 }
1435 return new DcmApplicationEntity(key); 1416 #endif
1436
1437 case EVR_CS: // code string
1438 return new DcmCodeString(key);
1439
1440 case EVR_DA: // date string
1441 return new DcmDate(key);
1442
1443 case EVR_DT: // date time string
1444 return new DcmDateTime(key);
1445
1446 case EVR_DS: // decimal string
1447 return new DcmDecimalString(key);
1448
1449 case EVR_IS: // integer string
1450 return new DcmIntegerString(key);
1451
1452 case EVR_TM: // time string
1453 return new DcmTime(key);
1454
1455 case EVR_UI: // unique identifier
1456 return new DcmUniqueIdentifier(key);
1457
1458 case EVR_ST: // short text
1459 return new DcmShortText(key);
1460
1461 case EVR_LO: // long string
1462 return new DcmLongString(key);
1463
1464 case EVR_LT: // long text
1465 return new DcmLongText(key);
1466
1467 case EVR_UT: // unlimited text
1468 return new DcmUnlimitedText(key);
1469
1470 case EVR_SH: // short string
1471 return new DcmShortString(key);
1472
1473 case EVR_PN: // person name
1474 return new DcmPersonName(key);
1475
1476 #if DCMTK_VERSION_NUMBER >= 361
1477 case EVR_UC: // unlimited characters
1478 return new DcmUnlimitedCharacters(key);
1479 #endif
1480
1481 #if DCMTK_VERSION_NUMBER >= 361
1482 case EVR_UR: // URI/URL
1483 return new DcmUniversalResourceIdentifierOrLocator(key);
1484 #endif
1485
1486
1487 /**
1488 * Numerical types
1489 **/
1490
1491 case EVR_SL: // signed long
1492 return new DcmSignedLong(key);
1493
1494 case EVR_SS: // signed short
1495 return new DcmSignedShort(key);
1496
1497 case EVR_UL: // unsigned long
1498 return new DcmUnsignedLong(key);
1499
1500 case EVR_US: // unsigned short
1501 return new DcmUnsignedShort(key);
1502
1503 case EVR_FL: // float single-precision
1504 return new DcmFloatingPointSingle(key);
1505
1506 case EVR_FD: // float double-precision
1507 return new DcmFloatingPointDouble(key);
1508
1509
1510 /**
1511 * Sequence types, should never occur at this point.
1512 **/
1513
1514 case EVR_SQ: // sequence of items
1515 throw OrthancException(ErrorCode_ParameterOutOfRange);
1516
1517
1518 /**
1519 * TODO
1520 **/
1521
1522 case EVR_AT: // attribute tag
1523 throw OrthancException(ErrorCode_NotImplemented);
1524
1525
1526 /**
1527 * Internal to DCMTK.
1528 **/
1529
1530 case EVR_xs: // SS or US depending on context
1531 case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name)
1532 case EVR_na: // na="not applicable", for data which has no VR
1533 case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor
1534 case EVR_item: // used internally for items
1535 case EVR_metainfo: // used internally for meta info datasets
1536 case EVR_dataset: // used internally for datasets
1537 case EVR_fileFormat: // used internally for DICOM files
1538 case EVR_dicomDir: // used internally for DICOMDIR objects
1539 case EVR_dirRecord: // used internally for DICOMDIR records
1540 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image
1541 case EVR_pixelItem: // used internally for pixel items in a compressed image
1542 case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR)
1543 case EVR_PixelData: // used internally for uncompressed pixeld data
1544 case EVR_OverlayData: // used internally for overlay data
1545 case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR
1546 default:
1547 break;
1548 }
1549
1550 throw OrthancException(ErrorCode_InternalError);
1551 } 1417 }
1552 1418
1553 1419
1554 1420
1555 void FromDcmtkBridge::FillElementWithString(DcmElement& element, 1421 void FromDcmtkBridge::FillElementWithString(DcmElement& element,
1556 const DicomTag& tag,
1557 const std::string& utf8Value, 1422 const std::string& utf8Value,
1558 bool decodeDataUriScheme, 1423 bool decodeDataUriScheme,
1559 Encoding dicomEncoding) 1424 Encoding dicomEncoding)
1560 { 1425 {
1561 std::string binary; 1426 std::string binary;
1576 { 1441 {
1577 binary = Toolbox::ConvertFromUtf8(utf8Value, dicomEncoding); 1442 binary = Toolbox::ConvertFromUtf8(utf8Value, dicomEncoding);
1578 decoded = &binary; 1443 decoded = &binary;
1579 } 1444 }
1580 1445
1581 DcmTag key(tag.GetGroup(), tag.GetElement()); 1446 if (IsBinaryTag(element.getTag()))
1582
1583 if (tag.IsPrivate() ||
1584 IsBinaryTag(key))
1585 { 1447 {
1586 bool ok; 1448 bool ok;
1587 1449
1588 switch (key.getEVR()) 1450 switch (element.getTag().getEVR())
1589 { 1451 {
1590 case EVR_OW: 1452 case EVR_OW:
1591 if (decoded->size() % sizeof(Uint16) != 0) 1453 if (decoded->size() % sizeof(Uint16) != 0)
1592 { 1454 {
1593 LOG(ERROR) << "A tag with OW VR must have an even number of bytes"; 1455 LOG(ERROR) << "A tag with OW VR must have an even number of bytes";
1617 1479
1618 bool ok = false; 1480 bool ok = false;
1619 1481
1620 try 1482 try
1621 { 1483 {
1622 switch (key.getEVR()) 1484 switch (element.getTag().getEVR())
1623 { 1485 {
1624 // http://support.dcmtk.org/docs/dcvr_8h-source.html 1486 // http://support.dcmtk.org/docs/dcvr_8h-source.html
1625 1487
1626 /** 1488 /**
1627 * TODO. 1489 * TODO.
1628 **/ 1490 **/
1629 1491
1630 case EVR_OB: // other byte 1492 case EVR_OB: // other byte
1631 case EVR_OF: // other float
1632 case EVR_OW: // other word 1493 case EVR_OW: // other word
1633 case EVR_AT: // attribute tag 1494 case EVR_AT: // attribute tag
1634 throw OrthancException(ErrorCode_NotImplemented); 1495 throw OrthancException(ErrorCode_NotImplemented);
1635 1496
1636 case EVR_UN: // unknown value representation 1497 case EVR_UN: // unknown value representation
1681 ok = element.putSint16(boost::lexical_cast<Sint16>(*decoded)).good(); 1542 ok = element.putSint16(boost::lexical_cast<Sint16>(*decoded)).good();
1682 break; 1543 break;
1683 } 1544 }
1684 1545
1685 case EVR_UL: // unsigned long 1546 case EVR_UL: // unsigned long
1547 #if DCMTK_VERSION_NUMBER >= 361
1548 case EVR_OL: // other long (requires byte-swapping)
1549 #endif
1686 { 1550 {
1687 ok = element.putUint32(boost::lexical_cast<Uint32>(*decoded)).good(); 1551 ok = element.putUint32(boost::lexical_cast<Uint32>(*decoded)).good();
1688 break; 1552 break;
1689 } 1553 }
1690 1554
1693 ok = element.putUint16(boost::lexical_cast<Uint16>(*decoded)).good(); 1557 ok = element.putUint16(boost::lexical_cast<Uint16>(*decoded)).good();
1694 break; 1558 break;
1695 } 1559 }
1696 1560
1697 case EVR_FL: // float single-precision 1561 case EVR_FL: // float single-precision
1562 case EVR_OF: // other float (requires byte swapping)
1698 { 1563 {
1699 ok = element.putFloat32(boost::lexical_cast<float>(*decoded)).good(); 1564 ok = element.putFloat32(boost::lexical_cast<float>(*decoded)).good();
1700 break; 1565 break;
1701 } 1566 }
1702 1567
1703 case EVR_FD: // float double-precision 1568 case EVR_FD: // float double-precision
1569 #if DCMTK_VERSION_NUMBER >= 361
1570 case EVR_OD: // other double (requires byte-swapping)
1571 #endif
1704 { 1572 {
1705 ok = element.putFloat64(boost::lexical_cast<double>(*decoded)).good(); 1573 ok = element.putFloat64(boost::lexical_cast<double>(*decoded)).good();
1706 break; 1574 break;
1707 } 1575 }
1708 1576
1748 ok = false; 1616 ok = false;
1749 } 1617 }
1750 1618
1751 if (!ok) 1619 if (!ok)
1752 { 1620 {
1621 DicomTag tag(element.getTag().getGroup(), element.getTag().getElement());
1753 throw OrthancException(ErrorCode_BadFileFormat, 1622 throw OrthancException(ErrorCode_BadFileFormat,
1754 "While creating a DICOM instance, tag (" + tag.Format() + 1623 "While creating a DICOM instance, tag (" + tag.Format() +
1755 ") has out-of-range value: \"" + (*decoded) + "\""); 1624 ") has out-of-range value: \"" + (*decoded) + "\"");
1756 } 1625 }
1757 } 1626 }
1758 1627
1759 1628
1760 DcmElement* FromDcmtkBridge::FromJson(const DicomTag& tag, 1629 DcmElement* FromDcmtkBridge::FromJson(const DicomTag& tag,
1761 const Json::Value& value, 1630 const Json::Value& value,
1762 bool decodeDataUriScheme, 1631 bool decodeDataUriScheme,
1763 Encoding dicomEncoding) 1632 Encoding dicomEncoding,
1633 const std::string& privateCreator)
1764 { 1634 {
1765 std::auto_ptr<DcmElement> element; 1635 std::auto_ptr<DcmElement> element;
1766 1636
1767 switch (value.type()) 1637 switch (value.type())
1768 { 1638 {
1769 case Json::stringValue: 1639 case Json::stringValue:
1770 element.reset(CreateElementForTag(tag)); 1640 element.reset(CreateElementForTag(tag, privateCreator));
1771 FillElementWithString(*element, tag, value.asString(), decodeDataUriScheme, dicomEncoding); 1641 FillElementWithString(*element, value.asString(), decodeDataUriScheme, dicomEncoding);
1772 break; 1642 break;
1773 1643
1774 case Json::nullValue: 1644 case Json::nullValue:
1775 element.reset(CreateElementForTag(tag)); 1645 element.reset(CreateElementForTag(tag, privateCreator));
1776 FillElementWithString(*element, tag, "", decodeDataUriScheme, dicomEncoding); 1646 FillElementWithString(*element, "", decodeDataUriScheme, dicomEncoding);
1777 break; 1647 break;
1778 1648
1779 case Json::arrayValue: 1649 case Json::arrayValue:
1780 { 1650 {
1781 DcmTag key(tag.GetGroup(), tag.GetElement()); 1651 DcmTag key(tag.GetGroup(), tag.GetElement());
1796 case Json::objectValue: 1666 case Json::objectValue:
1797 { 1667 {
1798 Json::Value::Members members = value[i].getMemberNames(); 1668 Json::Value::Members members = value[i].getMemberNames();
1799 for (Json::Value::ArrayIndex j = 0; j < members.size(); j++) 1669 for (Json::Value::ArrayIndex j = 0; j < members.size(); j++)
1800 { 1670 {
1801 item->insert(FromJson(ParseTag(members[j]), value[i][members[j]], decodeDataUriScheme, dicomEncoding)); 1671 item->insert(FromJson(ParseTag(members[j]), value[i][members[j]], decodeDataUriScheme, dicomEncoding, privateCreator));
1802 } 1672 }
1803 break; 1673 break;
1804 } 1674 }
1805 1675
1806 case Json::arrayValue: 1676 case Json::arrayValue:
1905 1775
1906 1776
1907 DcmDataset* FromDcmtkBridge::FromJson(const Json::Value& json, // Encoded using UTF-8 1777 DcmDataset* FromDcmtkBridge::FromJson(const Json::Value& json, // Encoded using UTF-8
1908 bool generateIdentifiers, 1778 bool generateIdentifiers,
1909 bool decodeDataUriScheme, 1779 bool decodeDataUriScheme,
1910 Encoding defaultEncoding) 1780 Encoding defaultEncoding,
1781 const std::string& privateCreator)
1911 { 1782 {
1912 std::auto_ptr<DcmDataset> result(new DcmDataset); 1783 std::auto_ptr<DcmDataset> result(new DcmDataset);
1913 Encoding encoding = ExtractEncoding(json, defaultEncoding); 1784 Encoding encoding = ExtractEncoding(json, defaultEncoding);
1914 1785
1915 SetString(*result, DCM_SpecificCharacterSet, GetDicomSpecificCharacterSet(encoding)); 1786 SetString(*result, DCM_SpecificCharacterSet, GetDicomSpecificCharacterSet(encoding));
1943 hasSopInstanceUid = true; 1814 hasSopInstanceUid = true;
1944 } 1815 }
1945 1816
1946 if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET) 1817 if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET)
1947 { 1818 {
1948 std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, encoding)); 1819 std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, encoding, privateCreator));
1949 const DcmTagKey& tag = element->getTag(); 1820 const DcmTagKey& tag = element->getTag();
1950 1821
1951 result->findAndDeleteElement(tag); 1822 result->findAndDeleteElement(tag);
1952 1823
1953 DcmElement* tmp = element.release(); 1824 DcmElement* tmp = element.release();
2266 /** 2137 /**
2267 * Deal with binary data (including PixelData). 2138 * Deal with binary data (including PixelData).
2268 **/ 2139 **/
2269 2140
2270 if (evr == EVR_OB || // other byte 2141 if (evr == EVR_OB || // other byte
2271 evr == EVR_OF || // other float
2272 #if DCMTK_VERSION_NUMBER >= 361
2273 evr == EVR_OD || // other double
2274 #endif
2275 #if DCMTK_VERSION_NUMBER >= 362
2276 evr == EVR_OL || // other long
2277 #endif
2278 evr == EVR_OW || // other word 2142 evr == EVR_OW || // other word
2279 evr == EVR_UN) // unknown value representation 2143 evr == EVR_UN) // unknown value representation
2280 { 2144 {
2281 Uint16* data16 = NULL; 2145 Uint16* data16 = NULL;
2282 Uint8* data = NULL; 2146 Uint8* data = NULL;
2462 visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); 2326 visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values);
2463 break; 2327 break;
2464 } 2328 }
2465 2329
2466 case EVR_UL: // unsigned long 2330 case EVR_UL: // unsigned long
2331 #if DCMTK_VERSION_NUMBER >= 362
2332 case EVR_OL:
2333 #endif
2467 { 2334 {
2468 DcmUnsignedLong& content = dynamic_cast<DcmUnsignedLong&>(element); 2335 DcmUnsignedLong& content = dynamic_cast<DcmUnsignedLong&>(element);
2469 2336
2470 std::vector<int64_t> values; 2337 std::vector<int64_t> values;
2471 values.reserve(content.getVM()); 2338 values.reserve(content.getVM());
2502 visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); 2369 visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values);
2503 break; 2370 break;
2504 } 2371 }
2505 2372
2506 case EVR_FL: // float single-precision 2373 case EVR_FL: // float single-precision
2374 case EVR_OF:
2507 { 2375 {
2508 DcmFloatingPointSingle& content = dynamic_cast<DcmFloatingPointSingle&>(element); 2376 DcmFloatingPointSingle& content = dynamic_cast<DcmFloatingPointSingle&>(element);
2509 2377
2510 std::vector<double> values; 2378 std::vector<double> values;
2511 values.reserve(content.getVM()); 2379 values.reserve(content.getVM());
2522 visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values); 2390 visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values);
2523 break; 2391 break;
2524 } 2392 }
2525 2393
2526 case EVR_FD: // float double-precision 2394 case EVR_FD: // float double-precision
2395 #if DCMTK_VERSION_NUMBER >= 361
2396 case EVR_OD:
2397 #endif
2527 { 2398 {
2528 DcmFloatingPointDouble& content = dynamic_cast<DcmFloatingPointDouble&>(element); 2399 DcmFloatingPointDouble& content = dynamic_cast<DcmFloatingPointDouble&>(element);
2529 2400
2530 std::vector<double> values; 2401 std::vector<double> values;
2531 values.reserve(content.getVM()); 2402 values.reserve(content.getVM());