# HG changeset patch # User Sebastien Jodogne # Date 1440083105 -7200 # Node ID b8dc2f855a83bdc4c388f4faf32a98b925035c7e # Parent d6a93e12b1c16d5a878262b559c06888ee91331d Preview of PDF files encapsulated in DICOM from Orthanc Explorer diff -r d6a93e12b1c1 -r b8dc2f855a83 Core/DicomFormat/DicomTag.h --- a/Core/DicomFormat/DicomTag.h Thu Aug 20 15:18:13 2015 +0200 +++ b/Core/DicomFormat/DicomTag.h Thu Aug 20 17:05:05 2015 +0200 @@ -107,6 +107,7 @@ static const DicomTag DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES(0x0018, 0x1090); static const DicomTag DICOM_TAG_IMAGES_IN_ACQUISITION(0x0020, 0x1002); static const DicomTag DICOM_TAG_PATIENT_NAME(0x0010, 0x0010); + static const DicomTag DICOM_TAG_ENCAPSULATED_DOCUMENT(0x0042, 0x0011); // The following is used for "modify/anonymize" operations static const DicomTag DICOM_TAG_SOP_CLASS_UID(0x0008, 0x0016); diff -r d6a93e12b1c1 -r b8dc2f855a83 NEWS --- a/NEWS Thu Aug 20 15:18:13 2015 +0200 +++ b/NEWS Thu Aug 20 17:05:05 2015 +0200 @@ -2,6 +2,7 @@ =============================== +* Preview of PDF files encapsulated in DICOM from Orthanc Explorer * Creation of DICOM files with encapsulated PDF through "/tools/create-dicom" * "limit" and "since" arguments while retrieving DICOM resources in the REST API * Support of "deflate" and "gzip" content-types in HTTP requests diff -r d6a93e12b1c1 -r b8dc2f855a83 OrthancExplorer/explorer.js --- a/OrthancExplorer/explorer.js Thu Aug 20 15:18:13 2015 +0200 +++ b/OrthancExplorer/explorer.js Thu Aug 20 17:05:05 2015 +0200 @@ -30,6 +30,19 @@ }; +function Refresh() +{ + if (currentPage == 'patient') + RefreshPatient(); + else if (currentPage == 'study') + RefreshStudy(); + else if (currentPage == 'series') + RefreshSeries(); + else if (currentPage == 'instance') + RefreshInstance(); +} + + $(document).ready(function() { var $tree = $('#dicom-tree'); $tree.tree({ @@ -45,6 +58,14 @@ $tree.tree('openNode', event.node, true); } ); + + currentPage = $.mobile.pageData.active; + currentUuid = $.mobile.pageData.uuid; + if (currentPage.length > 0 && + currentUuid.length > 0) + { + Refresh(); + } }); @@ -586,14 +607,7 @@ if ('uuid' in $.mobile.pageData && currentPage == $.mobile.pageData.active && currentUuid != $.mobile.pageData.uuid) { - if (currentPage == 'patient') - RefreshPatient(); - else if (currentPage == 'study') - RefreshStudy(); - else if (currentPage == 'series') - RefreshSeries(); - else if (currentPage == 'instance') - RefreshInstance(); + Refresh(); } }); }); @@ -686,34 +700,43 @@ $('#instance-preview').live('click', function(e) { if ($.mobile.pageData) { - GetResource('/instances/' + $.mobile.pageData.uuid + '/frames', function(frames) { - if (frames.length == 1) - { - // Viewing a single-frame image - jQuery.slimbox('../instances/' + $.mobile.pageData.uuid + '/preview', '', { - overlayFadeDuration : 1, - resizeDuration : 1, - imageFadeDuration : 1 - }); - } - else - { - // Viewing a multi-frame image + var pdf = '../instances/' + $.mobile.pageData.uuid + '/pdf'; + $.ajax({ + url: pdf, + cache: false, + success: function(s) { + window.location.assign(pdf); + }, + error: function() { + GetResource('/instances/' + $.mobile.pageData.uuid + '/frames', function(frames) { + if (frames.length == 1) + { + // Viewing a single-frame image + jQuery.slimbox('../instances/' + $.mobile.pageData.uuid + '/preview', '', { + overlayFadeDuration : 1, + resizeDuration : 1, + imageFadeDuration : 1 + }); + } + else + { + // Viewing a multi-frame image - var images = []; - for (var i = 0; i < frames.length; i++) { - images.push([ '../instances/' + $.mobile.pageData.uuid + '/frames/' + i + '/preview' ]); - } + var images = []; + for (var i = 0; i < frames.length; i++) { + images.push([ '../instances/' + $.mobile.pageData.uuid + '/frames/' + i + '/preview' ]); + } - jQuery.slimbox(images, 0, { - overlayFadeDuration : 1, - resizeDuration : 1, - imageFadeDuration : 1, - loop : true + jQuery.slimbox(images, 0, { + overlayFadeDuration : 1, + resizeDuration : 1, + imageFadeDuration : 1, + loop : true + }); + } }); } }); - } }); diff -r d6a93e12b1c1 -r b8dc2f855a83 OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Thu Aug 20 15:18:13 2015 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Thu Aug 20 17:05:05 2015 +0200 @@ -1050,6 +1050,20 @@ } + static void ExtractPdf(RestApiGetCall& call) + { + const std::string id = call.GetUriComponent("id", ""); + + std::string pdf; + ServerContext::DicomCacheLocker locker(OrthancRestApi::GetContext(call), id); + + if (locker.GetDicom().ExtractPdf(pdf)) + { + call.GetOutput().AnswerBuffer(pdf, "application/pdf"); + return; + } + } + void OrthancRestApi::RegisterResources() { @@ -1093,6 +1107,7 @@ Register("/instances/{id}/frames/{frame}/image-uint16", GetImage); Register("/instances/{id}/frames/{frame}/image-int16", GetImage); Register("/instances/{id}/frames/{frame}/matlab", GetMatlabImage); + Register("/instances/{id}/pdf", ExtractPdf); Register("/instances/{id}/preview", GetImage); Register("/instances/{id}/image-uint8", GetImage); Register("/instances/{id}/image-uint16", GetImage); diff -r d6a93e12b1c1 -r b8dc2f855a83 OrthancServer/ParsedDicomFile.cpp --- a/OrthancServer/ParsedDicomFile.cpp Thu Aug 20 15:18:13 2015 +0200 +++ b/OrthancServer/ParsedDicomFile.cpp Thu Aug 20 17:05:05 2015 +0200 @@ -966,7 +966,9 @@ DcmTagKey k(tag.GetGroup(), tag.GetElement()); DcmDataset& dataset = *pimpl_->file_->getDataset(); - if (FromDcmtkBridge::IsPrivateTag(tag)) + if (FromDcmtkBridge::IsPrivateTag(tag) || + tag == DICOM_TAG_PIXEL_DATA || + tag == DICOM_TAG_ENCAPSULATED_DOCUMENT) { const Uint8* data = NULL; // This is freed in the destructor of the dataset long unsigned int count = 0; @@ -1000,7 +1002,7 @@ } std::auto_ptr v(FromDcmtkBridge::ConvertLeafElement(*element, pimpl_->encoding_)); - + if (v.get() == NULL) { value = ""; @@ -1009,7 +1011,7 @@ { value = v->AsString(); } - + return true; } } @@ -1471,4 +1473,39 @@ } } + + bool ParsedDicomFile::ExtractPdf(std::string& pdf) + { + std::string sop, mime; + + if (!GetTagValue(sop, DICOM_TAG_SOP_CLASS_UID) || + !GetTagValue(mime, FromDcmtkBridge::Convert(DCM_MIMETypeOfEncapsulatedDocument)) || + sop != UID_EncapsulatedPDFStorage || + mime != "application/pdf") + { + return false; + } + + if (!GetTagValue(pdf, DICOM_TAG_ENCAPSULATED_DOCUMENT)) + { + return false; + } + + // Strip the possible pad byte at the end of file, because the + // encapsulated documents must always have an even length. The PDF + // format expects files to end with %%EOF followed by CR/LF. If + // the last character of the file is not a CR or LF, we assume it + // is a pad byte and remove it. + if (pdf.size() > 0) + { + char last = *pdf.rbegin(); + + if (last != 10 && last != 13) + { + pdf.resize(pdf.size() - 1); + } + } + + return true; + } } diff -r d6a93e12b1c1 -r b8dc2f855a83 OrthancServer/ParsedDicomFile.h --- a/OrthancServer/ParsedDicomFile.h Thu Aug 20 15:18:13 2015 +0200 +++ b/OrthancServer/ParsedDicomFile.h Thu Aug 20 17:05:05 2015 +0200 @@ -128,6 +128,8 @@ bool HasTag(const DicomTag& tag) const; void EmbedPdf(const std::string& pdf); + + bool ExtractPdf(std::string& pdf); }; }