changeset 2230:cbad54feb881

added DicomStructuredReport::Render()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 25 Apr 2025 17:50:05 +0200
parents 8cbf31382b7b
children 2161277b4586
files OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp OrthancStone/Sources/Toolbox/DicomStructuredReport.h
diffstat 2 files changed, 188 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp	Fri Apr 25 17:39:22 2025 +0200
+++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp	Fri Apr 25 17:50:05 2025 +0200
@@ -23,8 +23,10 @@
 
 #include "DicomStructuredReport.h"
 
+#include "../Scene2D/ScenePoint2D.h"
+#include "../Fonts/GlyphBitmapAlphabet.h"
+#include "BitmapLayout.h"
 #include "StoneToolbox.h"
-#include "../Scene2D/ScenePoint2D.h"
 
 #include <ChunkedBuffer.h>
 #include <OrthancException.h>
@@ -841,4 +843,183 @@
     Flatten(buffer, textualReport_, "");
     buffer.Flatten(target);
   }
+
+
+  namespace
+  {
+    class TextWriter : public boost::noncopyable
+    {
+    private:
+      BitmapLayout        layout_;
+      Color               highlightColor_;
+      Color               normalColor_;
+
+      FontRenderer&       font_;
+      GlyphBitmapAlphabet alphabet_;
+      int                 x_;
+      int                 y_;
+      unsigned int        maxHeight_;
+
+    public:
+      TextWriter(FontRenderer& font,
+                 const Color& highlightColor,
+                 const Color& normalColor) :
+        highlightColor_(highlightColor),
+        normalColor_(normalColor),
+        font_(font),
+        x_(0),
+        y_(0),
+        maxHeight_(0)
+      {
+      }
+
+      enum Move
+      {
+        Move_None,
+        Move_SmallInterline,
+        Move_LargeInterline
+      };
+
+      const Orthanc::ImageAccessor& Write(const std::string& s,
+                                          Move mode)
+      {
+        std::unique_ptr<Orthanc::ImageAccessor> block(alphabet_.RenderColorText(font_, s, highlightColor_, normalColor_));
+        const Orthanc::ImageAccessor& item = layout_.AddBlock(x_, y_, block.release());
+        maxHeight_ = std::max(maxHeight_, item.GetHeight());
+
+        switch (mode)
+        {
+          case Move_None:
+            break;
+
+          case Move_SmallInterline:
+            y_ += maxHeight_ + maxHeight_ / 4;
+            break;
+
+          case Move_LargeInterline:
+            y_ += maxHeight_ + maxHeight_;
+            break;
+
+          default:
+            throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        return item;
+      }
+
+      void SetX(int x)
+      {
+        x_ = x;
+      }
+
+      unsigned int GetX() const
+      {
+        return x_;
+      }
+
+      Orthanc::ImageAccessor* Render(Orthanc::PixelFormat format) const
+      {
+        return layout_.Render(format);
+      }
+    };
+  }
+
+
+  static void Explore(TextWriter& writer,
+                      const Json::Value& node,
+                      unsigned int maxLineWidth)
+  {
+    assert(node.type() == Json::arrayValue);
+
+    const unsigned int x = writer.GetX();
+
+    for (Json::ArrayIndex i = 0; i < node.size(); i++)
+    {
+      assert(node[i].type() == Json::arrayValue);
+      assert(node[i].size() == 2 || node[i].size() == 3);
+      assert(node[i][0].type() == Json::stringValue);
+      assert(node[i][1].type() == Json::stringValue || node[i][1].type() == Json::nullValue);
+      assert(node[i].size() == 2 || node[i][2].type() == Json::arrayValue);
+
+      std::string s = "\021" + boost::lexical_cast<std::string>(i + 1) + ".  ";
+      const Orthanc::ImageAccessor& label = writer.Write(s, TextWriter::Move_None);
+
+      std::string text = "\021" + node[i][0].asString();
+
+      if (node[i][1].type() == Json::stringValue)
+      {
+        text += ":\022 " + node[i][1].asString();
+      }
+
+      std::string indented;
+      GlyphAlphabet::IndentUtf8(indented, text, maxLineWidth, false);
+
+      writer.SetX(x + label.GetWidth());
+      writer.Write(text, TextWriter::Move_SmallInterline);
+
+      if (node[i].size() == 3)
+      {
+        Explore(writer, node[i][2], std::max(60u, maxLineWidth - 10u));
+      }
+
+      writer.SetX(x);
+    }
+  }
+
+
+  Orthanc::ImageAccessor* DicomStructuredReport::Render(FontRenderer& font,
+                                                        const Color& highlightColor,
+                                                        const Color& normalColor) const
+  {
+    TextWriter writer(font, highlightColor, normalColor);
+
+    writer.Write(GetTitle(), TextWriter::Move_LargeInterline);
+
+    std::string s = GetMainDicomTags().GetStringValue(Orthanc::DICOM_TAG_SERIES_TIME, "", false);
+    size_t pos = s.find('.');
+    if (pos != std::string::npos)
+    {
+      s = s.substr(0, pos);
+    }
+
+    s = "\021Series Date Time:\022 " + GetMainDicomTags().GetStringValue(Orthanc::DICOM_TAG_SERIES_DATE, "", false) + " at " + s;
+    writer.Write(s, TextWriter::Move_LargeInterline);
+
+    writer.Write("\021Patient's name:\022 " +
+                 GetMainDicomTags().GetStringValue(Orthanc::DICOM_TAG_PATIENT_NAME, "", false),
+                 TextWriter::Move_SmallInterline);
+    writer.Write("\021Patient ID:\022 " +
+                 GetMainDicomTags().GetStringValue(Orthanc::DICOM_TAG_PATIENT_ID, "", false),
+                 TextWriter::Move_SmallInterline);
+    writer.Write("\021Patient's Birth Date:\022 " +
+                 GetMainDicomTags().GetStringValue(Orthanc::DICOM_TAG_PATIENT_BIRTH_DATE, "", false),
+                 TextWriter::Move_SmallInterline);
+    writer.Write("\021Patient's Sex:\022 " +
+                 GetMainDicomTags().GetStringValue(Orthanc::DICOM_TAG_PATIENT_SEX, "", false),
+                 TextWriter::Move_LargeInterline);
+
+    writer.Write("\021Study Description:\022 " +
+                 GetMainDicomTags().GetStringValue(Orthanc::DICOM_TAG_STUDY_DESCRIPTION, "", false),
+                 TextWriter::Move_SmallInterline);
+    writer.Write("\021Study ID:\022 " +
+                 GetMainDicomTags().GetStringValue(Orthanc::DICOM_TAG_STUDY_ID, "", false),
+                 TextWriter::Move_SmallInterline);
+    writer.Write("\021Accession Number:\022 " +
+                 GetMainDicomTags().GetStringValue(Orthanc::DICOM_TAG_ACCESSION_NUMBER, "", false),
+                 TextWriter::Move_SmallInterline);
+    writer.Write("\021Referring Physician's Name:\022 " +
+                 GetMainDicomTags().GetStringValue(Orthanc::DICOM_TAG_REFERRING_PHYSICIAN_NAME, "", false),
+                 TextWriter::Move_LargeInterline);
+
+    writer.Write("\021Completion Flag:\022 " +
+                 GetMainDicomTags().GetStringValue(Orthanc::DicomTag(0x0040, 0xa491), "", false),
+                 TextWriter::Move_SmallInterline);
+    writer.Write("\021Verification Flag:\022 " +
+                 GetMainDicomTags().GetStringValue(Orthanc::DicomTag(0x0040, 0xa493), "", false),
+                 TextWriter::Move_LargeInterline);
+
+    Explore(writer, GetTextualReport(), 160);
+
+    return writer.Render(Orthanc::PixelFormat_RGB24);
+  }
 }
--- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.h	Fri Apr 25 17:39:22 2025 +0200
+++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.h	Fri Apr 25 17:50:05 2025 +0200
@@ -31,6 +31,8 @@
 #  error Support for DCMTK must be enabled
 #endif
 
+#include "../Fonts/FontRenderer.h"
+#include "../Scene2D/Color.h"
 #include "../Scene2D/ScenePoint2D.h"
 
 #include <DicomParsing/ParsedDicomFile.h>
@@ -335,5 +337,9 @@
                               const std::string& sopInstanceUid) const;
 
     void FlattenTextualReport(std::string& target) const;
+
+    Orthanc::ImageAccessor* Render(FontRenderer& font,
+                                   const Color& highlightColor,
+                                   const Color& normalColor) const;
   };
 }