From 99b2660fb05ea5e057c69d3f6f0780448e9399c1 Mon Sep 17 00:00:00 2001
From: Tor-Einar Skog <tor-einar.skog@nibio.no>
Date: Fri, 17 Feb 2023 10:50:55 +0100
Subject: [PATCH] Add methods for base64 encoding of images in desc

---
 src/vipscore_common/vips_model.py | 109 +++++++++++++++++++++++++-----
 1 file changed, 93 insertions(+), 16 deletions(-)

diff --git a/src/vipscore_common/vips_model.py b/src/vipscore_common/vips_model.py
index 38e02f9..f4bab1c 100755
--- a/src/vipscore_common/vips_model.py
+++ b/src/vipscore_common/vips_model.py
@@ -1,21 +1,20 @@
 #!/usr/bin/python3
 
 """
-Copyright (c) 2023 NIBIO <http://www.nibio.no/>. 
-
-This file is part of VIPSCore-Python-Common.
-VIPSCore-Python-Common is free software: you can redistribute it and/or modify
-it under the terms of the NIBIO Open Source License as published by 
-NIBIO, either version 1 of the License, or (at your option) any
-later version.
- 
-VIPSCore-Python-Common is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-NIBIO Open Source License for more details.
-
-You should have received a copy of the NIBIO Open Source License
-along with VIPSCore-Python-Common.  If not, see <http://www.nibio.no/licenses/>.
+    Copyright (C) 2023  NIBIO
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
 """
 
 # All models in VIPSCore-Python must extend this abstract class
@@ -24,6 +23,9 @@ along with VIPSCore-Python-Common.  If not, see <http://www.nibio.no/licenses/>.
 from abc import ABC, abstractmethod, abstractproperty
 from .entities import Result, ModelConfiguration
 
+import re, mimetypes, base64
+mimetypes.init()
+
 class VIPSModel(ABC):
 
     # Default language is English
@@ -90,4 +92,79 @@ class VIPSModel(ABC):
         """Technical manual for this model, in the specified language  (<a href="http://www.loc.gov/standards/iso639-2/php/English_list.php">ISO-639-2</a>)"""
         pass
 
-   
\ No newline at end of file
+    """
+    Util methods to be able to serve images as part of the model description
+    """
+    tag_matcher = None
+    attribute_matcher = None
+    image_path = None
+
+    def get_tag_matcher(self):
+        """
+        Get the regex matcher to find {{}} pseudotags
+        """
+        if self.tag_matcher is None:
+            self.tag_matcher = re.compile("\\{\\{(.*?)\\}\\}",re.DOTALL)
+        return self.tag_matcher
+    
+    def get_attribute_matcher(self):
+        """
+        Get the regex matcher to find attributes inside the {{}} pseudotag
+        """
+        if self.attribute_matcher is None:
+            self.attribute_matcher = re.compile("([^=\\s]+)=\"([^\"]+)\"")
+        return self.attribute_matcher
+
+    def replace_match(self, tag_match):
+        """
+        Replace contents of pseudotags with <img/> element
+        """
+
+        ar = self.get_attribute_matcher()
+        tag_text = "<img"
+        tag_contents = tag_match.group(1)
+        file_attr_name_found = False
+        # Iterating the attributes ("filename", "description", "float"), adding to img tag
+        for attribute_match in ar.finditer(tag_contents):
+            attr_name = attribute_match.group(1)
+            attr_val = attribute_match.group(2)
+            # Analyzing each tag
+            if attr_name == "filename":
+                file_attr_name_found = True
+                file_path = "%s%s" % (self.image_path, attr_val )
+                # https://docs.python.org/3/library/mimetypes.html
+                mime_type = mimetypes.guess_type("%s%s" % (self.image_path, attr_val ))[0]
+                f = open(file_path,"rb")
+                b64encoded = base64.b64encode(f.read()).decode("utf-8")
+                f.close()
+                tag_text = "%s src=\"data:%s;base64,%s\"" % (tag_text, mime_type, b64encoded)
+            if attr_name == "description":
+                tag_text = "%s alt=\"%s\" title=\"%s\"" % (tag_text, attr_val, attr_val)
+            if attr_name == "float":
+                tag_text = "%s style=\"float: %s\"" % (tag_text, attr_val)
+        tag_text = "%s class=\"img-responsive\"/>" % tag_text
+
+        if file_attr_name_found:
+            return tag_text
+        else:
+            return "ERROR PARSING IMAGE PSEUDOTAG: MISSING filepath."
+
+    def get_text_with_base64_encoded_images(self, text, image_path):
+        """
+        Parsing text with reference to images. Image template tag format is:
+        {{filename="/path/to/filename.jpg" description="Sample description" float="[CSSFloat property]"}}
+        {{filename="/1348485230424.jpg" description="Chart for apple scab model" float="none"}}
+
+        These tags are parsed and transformed to html &lt;img/&gt; tags, and the image files 
+        are base64 encoded and embedded. The only mandatory parameter is filename. It refers to the
+        file relative to the Model jar in which it is placed.
+        @param text the description text
+        @param image_path the path where images are stored (absolute, must be figured out with pathlib.Path(__file__).parent in the calling VIPSModel)
+        @return The same text with embedded image(s)
+        """
+             
+        self.image_path = image_path
+        return re.sub(self.get_tag_matcher(), self.replace_match, text)
+        
+        
+        
\ No newline at end of file
-- 
GitLab