From 4682b91364247aafe66b6af472e321511e115e7c Mon Sep 17 00:00:00 2001
From: albert-github <albert.tests@gmail.com>
Date: Thu, 9 Aug 2018 19:49:58 +0200
Subject: Inline images

Create the possibility of inline images with the `\image` command by means of the option `inline`.
---
 doc/commands.doc            |  6 ++++-
 src/docbookvisitor.cpp      | 10 ++++----
 src/docparser.cpp           | 47 +++++++++++++++++++++++++++++++----
 src/docparser.h             |  4 ++-
 src/htmldocvisitor.cpp      | 60 +++++++++++++++++++++++++++++++++++++--------
 src/latexdocvisitor.cpp     | 59 ++++++++++++++++++++++++++++++++------------
 src/printdocvisitor.h       |  2 +-
 src/rtfdocvisitor.cpp       | 55 +++++++++++++++++++++++++++--------------
 src/rtfdocvisitor.h         |  4 +--
 src/xmldocvisitor.cpp       |  5 ++--
 templates/html/doxygen.css  |  2 +-
 templates/latex/doxygen.sty |  6 +++++
 12 files changed, 198 insertions(+), 62 deletions(-)

diff --git a/doc/commands.doc b/doc/commands.doc
index 6c680ee..a9973ce 100644
--- a/doc/commands.doc
+++ b/doc/commands.doc
@@ -3052,7 +3052,7 @@ class Receiver
               \ref cmddocbookonly "\\docbookonly".
 
 <hr>
-\section cmdimage \\image <format> <file> ["caption"] [<sizeindication>=<size>]
+\section cmdimage \\image['{'[option]'}'] <format> <file> ["caption"] [<sizeindication>=<size>]
 
   \addindex \\image
   Inserts an image into the documentation. This command is format
@@ -3084,6 +3084,10 @@ class Receiver
   The size specifier in \LaTeX (for example `10cm` or
   `4in` or a symbolic width like `\\textwidth`).
 
+  Currently only the option `inline` is supported. In case the option `inline` is
+  specified the image is placed "in the line", when a caption s present it is shown 
+  in HTML as tooltip (ignored for the other formats).
+
   Here is example of a comment block:
 
 \verbatim
diff --git a/src/docbookvisitor.cpp b/src/docbookvisitor.cpp
index ab10da0..78d19a4 100644
--- a/src/docbookvisitor.cpp
+++ b/src/docbookvisitor.cpp
@@ -36,7 +36,7 @@
 #include "htmlentity.h"
 #include "plantuml.h"
 
-static void visitPreStart(FTextStream &t, const bool hasCaption, QCString name,  QCString width,  QCString height)
+static void visitPreStart(FTextStream &t, const bool hasCaption, QCString name,  QCString width,  QCString height, const bool inlineImage = FALSE)
 {
   QCString tmpStr;
   t << "    <figure>" << endl;
@@ -50,7 +50,7 @@ static void visitPreStart(FTextStream &t, const bool hasCaption, QCString name,
   }
   else
   {
-    t << " width=\"50%\"";
+    if (!inlineImage) t << " width=\"50%\"";
   }
   if (!height.isEmpty())
   {
@@ -65,7 +65,7 @@ static void visitPreStart(FTextStream &t, const bool hasCaption, QCString name,
   }
 }
 
-static void visitPostEnd(FTextStream &t, const bool hasCaption)
+static void visitPostEnd(FTextStream &t, const bool hasCaption, const bool inlineImage = FALSE)
 {
   t << endl;
   if (hasCaption)
@@ -910,7 +910,7 @@ void DocbookDocVisitor::visitPre(DocImage *img)
     {
       baseName=baseName.right(baseName.length()-i-1);
     }
-    visitPreStart(m_t, img -> hasCaption(), baseName, img -> width(), img -> height());
+    visitPreStart(m_t, img -> hasCaption(), baseName, img -> width(), img -> height(),img -> isInlineImage());
   }
   else
   {
@@ -924,7 +924,7 @@ void DocbookDocVisitor::visitPost(DocImage *img)
   if (img->type()==DocImage::DocBook)
   {
     if (m_hide) return;
-    visitPostEnd(m_t, img -> hasCaption());
+    visitPostEnd(m_t, img -> hasCaption(),img -> isInlineImage());
     // copy the image to the output dir
     QCString baseName=img->name();
     int i;
diff --git a/src/docparser.cpp b/src/docparser.cpp
index 7e050b3..4cf7c4e 100644
--- a/src/docparser.cpp
+++ b/src/docparser.cpp
@@ -2962,10 +2962,10 @@ void DocVhdlFlow::parse()
 //---------------------------------------------------------------------------
 
 DocImage::DocImage(DocNode *parent,const HtmlAttribList &attribs,const QCString &name,
-                   Type t,const QCString &url) : 
+                   Type t,const QCString &url, const bool inlineImage) : 
       m_attribs(attribs), m_name(name), 
       m_type(t), m_relPath(g_relPath),
-      m_url(url)
+      m_url(url), m_inlineImage(inlineImage)
 {
   m_parent = parent;
 }
@@ -5065,13 +5065,50 @@ void DocPara::handleIncludeOperator(const QCString &cmdName,DocIncOperator::Type
 void DocPara::handleImage(const QCString &cmdName)
 {
   QCString saveCmdName = cmdName;
+  bool inlineImage = FALSE;
 
   int tok=doctokenizerYYlex();
   if (tok!=TK_WHITESPACE)
   {
-    warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
+    if (tok==TK_WORD)
+    {
+      if (g_token->name == "{")
+      {
+        while ((tok=doctokenizerYYlex())==TK_WHITESPACE);
+        if (g_token->name != "}") // non-empty option string
+	{
+          if (g_token->name.lower() != "inline")
+          {
+            warn_doc_error(g_fileName,doctokenizerYYlineno,"currently only 'inline' suported as option of %s command",
+              qPrint(saveCmdName));
+          }
+          else
+          {
+            inlineImage = TRUE;
+          }
+          while ((tok=doctokenizerYYlex())==TK_WHITESPACE);
+	}
+        if (!((tok==TK_WORD) && (g_token->name == "}")))
+        {
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"expected closing '}' at option of %s command",
+            qPrint(saveCmdName));
+          return;
+        }
+        tok=doctokenizerYYlex();
+        if (tok!=TK_WHITESPACE)
+        {
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command with option",
+            qPrint(saveCmdName));
+          return;
+        }
+      }
+    }
+    else
+    {
+      warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
         qPrint(saveCmdName));
-    return;
+      return;
+    }
   }
   tok=doctokenizerYYlex();
   if (tok!=TK_WORD && tok!=TK_LNKWORD)
@@ -5110,7 +5147,7 @@ void DocPara::handleImage(const QCString &cmdName)
     return;
   }
   HtmlAttribList attrList;
-  DocImage *img = new DocImage(this,attrList,findAndCopyImage(g_token->name,t),t);
+  DocImage *img = new DocImage(this,attrList,findAndCopyImage(g_token->name,t),t,"",inlineImage);
   m_children.append(img);
   img->parse();
 }
diff --git a/src/docparser.h b/src/docparser.h
index d7390c2..69259d6 100644
--- a/src/docparser.h
+++ b/src/docparser.h
@@ -737,7 +737,7 @@ class DocImage : public CompAccept<DocImage>
   public:
     enum Type { Html, Latex, Rtf, DocBook };
     DocImage(DocNode *parent,const HtmlAttribList &attribs,
-             const QCString &name,Type t,const QCString &url=QCString());
+             const QCString &name,Type t,const QCString &url=QCString(), const bool inlineImage = TRUE);
     Kind kind() const           { return Kind_Image; }
     Type type() const           { return m_type; }
     QCString name() const       { return m_name; }
@@ -746,6 +746,7 @@ class DocImage : public CompAccept<DocImage>
     QCString height() const     { return m_height; }
     QCString relPath() const    { return m_relPath; }
     QCString url() const        { return m_url; }
+    bool isInlineImage() const  { return m_inlineImage; }
     const HtmlAttribList &attribs() const { return m_attribs; }
     void parse();
 
@@ -757,6 +758,7 @@ class DocImage : public CompAccept<DocImage>
     QCString  m_height;
     QCString  m_relPath;
     QCString  m_url;
+    bool      m_inlineImage;
 };
 
 /** Node representing a dot file */
diff --git a/src/htmldocvisitor.cpp b/src/htmldocvisitor.cpp
index 6a9c142..6bd5347 100644
--- a/src/htmldocvisitor.cpp
+++ b/src/htmldocvisitor.cpp
@@ -1485,7 +1485,19 @@ void HtmlDocVisitor::visitPre(DocImage *img)
 {
   if (img->type()==DocImage::Html)
   {
-    forceEndParagraph(img);
+    bool inlineImage = img->isInlineImage();
+    bool typeSVG = FALSE;
+
+    QCString url = img->url();
+    if (url.isEmpty())
+    {
+      typeSVG = (img->name().right(4)==".svg");
+    }
+    else
+    {
+      typeSVG = (url.right(4)==".svg");
+    }
+    if (!inlineImage && !typeSVG) forceEndParagraph(img);
     if (m_hide) return;
     QString baseName=img->name();
     int i;
@@ -1493,8 +1505,7 @@ void HtmlDocVisitor::visitPre(DocImage *img)
     {
       baseName=baseName.right(baseName.length()-i-1);
     }
-    m_t << "<div class=\"image\">" << endl;
-    QCString url = img->url();
+    if (!inlineImage && !typeSVG) m_t << "<div class=\"image\">" << endl;
     QCString sizeAttribs;
     if (!img->width().isEmpty())
     {
@@ -1516,7 +1527,7 @@ void HtmlDocVisitor::visitPre(DocImage *img)
       {
         m_t << "<img src=\"" << img->relPath() << img->name() << "\" alt=\""
             << baseName << "\"" << sizeAttribs << htmlAttribsToString(img->attribs())
-            << "/>" << endl;
+	    << (inlineImage ? " class=\"inline\"" : "/>\n");
       }
     }
     else
@@ -1530,13 +1541,24 @@ void HtmlDocVisitor::visitPre(DocImage *img)
       {
         m_t << "<img src=\"" << correctURL(url,img->relPath()) << "\""
             << sizeAttribs << htmlAttribsToString(img->attribs(), TRUE)
-            << "/>" << endl;
+	    << (inlineImage ? " class=\"inline\"" : "/>\n");
       }
     }
     if (img->hasCaption())
     {
-      m_t << "<div class=\"caption\">" << endl;
-      m_t << getHtmlDirEmbedingChar(getTextDirByConfig(img));
+      if (inlineImage)
+      {
+       m_t << " title=\"";
+      }
+      else
+      {
+        m_t << "<div class=\"caption\">" << endl;
+        m_t << getHtmlDirEmbedingChar(getTextDirByConfig(img));
+      }
+    }
+    else if (inlineImage)
+    {
+      m_t << "/>" << endl;
     }
   }
   else // other format -> skip
@@ -1551,12 +1573,30 @@ void HtmlDocVisitor::visitPost(DocImage *img)
   if (img->type()==DocImage::Html)
   {
     if (m_hide) return;
+    bool inlineImage = img->isInlineImage();
+    bool typeSVG = FALSE;
+    QCString url = img->url();
+    if (url.isEmpty())
+    {
+      typeSVG = (img->name().right(4)==".svg");
+    }
+    else
+    {
+      typeSVG = (url.right(4)==".svg");
+    }
+    //if (!inlineImage && img->hasCaption())
     if (img->hasCaption())
     {
-      m_t << "</div>";
+      if (inlineImage)
+        m_t << "\"/>\n ";
+      else
+        m_t << "</div>";
+    }
+    if (!inlineImage && !typeSVG)
+    {
+      m_t << "</div>" << endl;
+      forceStartParagraph(img);
     }
-    m_t << "</div>" << endl;
-    forceStartParagraph(img);
   }
   else // other format
   {
diff --git a/src/latexdocvisitor.cpp b/src/latexdocvisitor.cpp
index d2c4c5d..03f9872 100644
--- a/src/latexdocvisitor.cpp
+++ b/src/latexdocvisitor.cpp
@@ -48,16 +48,23 @@ static const char *getSectionName(int level)
   return secLabels[QMIN(maxLevels-1,l)];
 }
 
-static void visitPreStart(FTextStream &t, const bool hasCaption, QCString name,  QCString width,  QCString height)
+static void visitPreStart(FTextStream &t, const bool hasCaption, QCString name,  QCString width,  QCString height, const bool inlineImage = FALSE)
 {
-    if (hasCaption)
+    if (inlineImage)
     {
-      t << "\n\\begin{DoxyImage}\n";
+      t << "\n\\begin{DoxyInlineImage}\n";
     }
     else
     {
-      t << "\n\\begin{DoxyImageNoCaption}\n"
-             "  \\mbox{";
+      if (hasCaption)
+      {
+        t << "\n\\begin{DoxyImage}\n";
+      }
+      else
+      {
+        t << "\n\\begin{DoxyImageNoCaption}\n"
+               "  \\mbox{";
+      }
     }
 
     t << "\\includegraphics";
@@ -80,7 +87,14 @@ static void visitPreStart(FTextStream &t, const bool hasCaption, QCString name,
     if (width.isEmpty() && height.isEmpty())
     {
       /* default setting */
-      t << "[width=\\textwidth,height=\\textheight/2,keepaspectratio=true]";
+      if (inlineImage)
+      {
+        t << "[height=\\baselineskip,keepaspectratio=true]";
+      }
+      else
+      {
+        t << "[width=\\textwidth,height=\\textheight/2,keepaspectratio=true]";
+      }
     }
     else
     {
@@ -91,21 +105,36 @@ static void visitPreStart(FTextStream &t, const bool hasCaption, QCString name,
 
     if (hasCaption)
     {
-      t << "\n\\doxyfigcaption{";
+      if (!inlineImage)
+      {
+        t << "\n\\doxyfigcaption{";
+      }
+      else
+      {
+        t << "%"; // to catch the caption
+      }
     }
 }
 
 
 
-static void visitPostEnd(FTextStream &t, const bool hasCaption)
+static void visitPostEnd(FTextStream &t, const bool hasCaption, const bool inlineImage = FALSE)
 {
-    t << "}\n"; // end mbox or caption
-    if (hasCaption)
+    if (inlineImage)
     {
-      t << "\\end{DoxyImage}\n";
+      t << "\n\\end{DoxyInlineImage}\n";
     }
-    else{
-      t << "\\end{DoxyImageNoCaption}\n";
+    else
+    {
+      t << "}\n"; // end mbox or caption
+      if (hasCaption)
+      {
+        t << "\\end{DoxyImage}\n";
+      }
+      else
+      {
+        t << "\\end{DoxyImageNoCaption}\n";
+      }
     }
 }
 
@@ -1280,7 +1309,7 @@ void LatexDocVisitor::visitPre(DocImage *img)
       gfxName=gfxName.left(gfxName.length()-4);
     }
 
-    visitPreStart(m_t,img->hasCaption(), gfxName, img->width(),  img->height());
+    visitPreStart(m_t,img->hasCaption(), gfxName, img->width(),  img->height(), img->isInlineImage());
   }
   else // other format -> skip
   {
@@ -1294,7 +1323,7 @@ void LatexDocVisitor::visitPost(DocImage *img)
   if (img->type()==DocImage::Latex)
   {
     if (m_hide) return;
-    visitPostEnd(m_t,img->hasCaption());
+    visitPostEnd(m_t,img->hasCaption(), img->isInlineImage());
   }
   else // other format
   {
diff --git a/src/printdocvisitor.h b/src/printdocvisitor.h
index d1dbb74..4d1d765 100644
--- a/src/printdocvisitor.h
+++ b/src/printdocvisitor.h
@@ -490,7 +490,7 @@ class PrintDocVisitor : public DocVisitor
         case DocImage::Rtf:     printf("rtf"); break;
         case DocImage::DocBook: printf("docbook"); break;
       }
-      printf("\" %s %s>\n",img->width().data(),img->height().data());
+      printf("\" %s %s inline=\"%s\">\n",img->width().data(),img->height().data(),img->isInlineImage() ? "yes" : "no");
     }
     void visitPost(DocImage *) 
     {
diff --git a/src/rtfdocvisitor.cpp b/src/rtfdocvisitor.cpp
index ec6d015..fa7fb64 100644
--- a/src/rtfdocvisitor.cpp
+++ b/src/rtfdocvisitor.cpp
@@ -1088,27 +1088,37 @@ void RTFDocVisitor::visitPost(DocHtmlHeader *)
 void RTFDocVisitor::visitPre(DocImage *img)
 {
   DBG_RTF("{\\comment RTFDocVisitor::visitPre(DocImage)}\n");
-  includePicturePreRTF(img->name(), img->type()==DocImage::Rtf, img->hasCaption());
+  includePicturePreRTF(img->name(), img->type()==DocImage::Rtf, img->hasCaption(), img->isInlineImage());
 }
-
-void RTFDocVisitor::includePicturePreRTF(const QCString name, const bool isTypeRTF, const bool hasCaption)
+void RTFDocVisitor::includePicturePreRTF(const QCString name, const bool isTypeRTF, const bool hasCaption, const bool inlineImage)
 {
   if (isTypeRTF)
   {
-    m_t << "\\par" << endl;
-    m_t << "{" << endl;
-    m_t << rtf_Style_Reset << endl;
-    if (hasCaption || m_lastIsPara) m_t << "\\par" << endl;
-    m_t << "\\pard \\qc { \\field\\flddirty {\\*\\fldinst  INCLUDEPICTURE \"";
+    if (!inlineImage)
+    {
+      m_t << "\\par" << endl;
+      m_t << "{" << endl;
+      m_t << rtf_Style_Reset << endl;
+      if (hasCaption || m_lastIsPara) m_t << "\\par" << endl;
+      m_t << "\\pard \\qc ";
+    }
+    m_t << "{ \\field\\flddirty {\\*\\fldinst  INCLUDEPICTURE \"";
     m_t << name;
     m_t << "\" \\\\d \\\\*MERGEFORMAT}{\\fldrslt Image}}" << endl;
-    m_t << "\\par" << endl;
-    if (hasCaption)
+    if (!inlineImage)
+    {
+      m_t << "\\par" << endl;
+      if (hasCaption)
+      {
+         m_t << "\\pard \\qc \\b";
+         m_t << "{Image \\field\\flddirty{\\*\\fldinst { SEQ Image \\\\*Arabic }}{\\fldrslt {\\noproof 1}} ";
+      }
+      m_lastIsPara=TRUE;
+    }
+    else
     {
-       m_t << "\\pard \\qc \\b";
-       m_t << "{Image \\field\\flddirty{\\*\\fldinst { SEQ Image \\\\*Arabic }}{\\fldrslt {\\noproof 1}} ";
+      if (hasCaption) m_t << "{\\comment "; // to prevent caption to be shown
     }
-    m_lastIsPara=TRUE;
   }
   else // other format -> skip
   {
@@ -1120,22 +1130,29 @@ void RTFDocVisitor::includePicturePreRTF(const QCString name, const bool isTypeR
 void RTFDocVisitor::visitPost(DocImage *img)
 {
   DBG_RTF("{\\comment RTFDocVisitor::visitPost(DocImage)}\n");
-  includePicturePostRTF(img->type()==DocImage::Rtf, img->hasCaption());
+  includePicturePostRTF(img->type()==DocImage::Rtf, img->hasCaption(), img->isInlineImage());
 }
 
-void RTFDocVisitor::includePicturePostRTF(const bool isTypeRTF, const bool hasCaption)
+void RTFDocVisitor::includePicturePostRTF(const bool isTypeRTF, const bool hasCaption, const bool inlineImage)
 {
   if (isTypeRTF)
   {
     if (m_hide) return;
-    if (hasCaption)
+    if (inlineImage)
     {
-       m_t << "}" <<endl;
-       m_t << "\\par}" <<endl;
+      if (hasCaption) m_t << " }";
     }
     else
     {
-       m_t << "}" <<endl;
+      if (hasCaption)
+      {
+        m_t << "}" <<endl;
+        m_t << "\\par}" <<endl;
+      }
+      else
+      {
+        m_t << "}" <<endl;
+      }
     }
   }
   else
diff --git a/src/rtfdocvisitor.h b/src/rtfdocvisitor.h
index 1e927e7..ad82067 100644
--- a/src/rtfdocvisitor.h
+++ b/src/rtfdocvisitor.h
@@ -152,8 +152,8 @@ class RTFDocVisitor : public DocVisitor
 
     void pushEnabled();
     void popEnabled();
-    void includePicturePreRTF(const QCString name, const bool isTypeRTF, const bool hasCaption);
-    void includePicturePostRTF(const bool isTypeRTF, const bool hasCaption);
+    void includePicturePreRTF(const QCString name, const bool isTypeRTF, const bool hasCaption, const bool inlineImage = FALSE);
+    void includePicturePostRTF(const bool isTypeRTF, const bool hasCaption, const bool inlineImage = FALSE);
     void writeDotFile(const QCString &fileName, const bool hasCaption);
     void writeDotFile(DocDotFile *);
     void writeMscFile(const QCString &fileName, const bool hasCaption);
diff --git a/src/xmldocvisitor.cpp b/src/xmldocvisitor.cpp
index 93765b1..8b00702 100644
--- a/src/xmldocvisitor.cpp
+++ b/src/xmldocvisitor.cpp
@@ -42,7 +42,7 @@ static void visitCaption(XmlDocVisitor *parent, QList<DocNode> children)
 static void visitPreStart(FTextStream &t, const char *cmd, const bool doCaption, 
                           XmlDocVisitor *parent, QList<DocNode> children, 
                           const QCString &name, bool writeType, DocImage::Type type, const QCString &width, 
-                          const QCString &height)
+                          const QCString &height, const bool inlineImage = FALSE)
 {
   t << "<" << cmd;
   if (writeType)
@@ -69,6 +69,7 @@ static void visitPreStart(FTextStream &t, const char *cmd, const bool doCaption,
   {
     t << " height=\"" << convertToXML(height) << "\"";
   }
+  if (inlineImage) t << " inline=\"yes\">";
   if (doCaption)
   {
     t << " caption=\"";
@@ -767,7 +768,7 @@ void XmlDocVisitor::visitPre(DocImage *img)
   {
     baseName=baseName.right(baseName.length()-i-1);
   }
-  visitPreStart(m_t, "image", FALSE, this, img->children(), baseName, TRUE, img->type(), img->width(), img->height());
+  visitPreStart(m_t, "image", FALSE, this, img->children(), baseName, TRUE, img->type(), img->width(), img->height(), img ->isInlineImage());
 
   // copy the image to the output dir
   FileDef *fd;
diff --git a/templates/html/doxygen.css b/templates/html/doxygen.css
index 8e87381..4b4c8ea 100644
--- a/templates/html/doxygen.css
+++ b/templates/html/doxygen.css
@@ -343,7 +343,7 @@ img.formulaDsp {
 	
 }
 
-img.formulaInl {
+img.formulaInl, img.inline {
 	vertical-align: middle;
 }
 
diff --git a/templates/latex/doxygen.sty b/templates/latex/doxygen.sty
index 7798d48..d0d255c 100644
--- a/templates/latex/doxygen.sty
+++ b/templates/latex/doxygen.sty
@@ -190,6 +190,12 @@
   \end{center}%
 }
 
+% Used by @image
+% (only if inline is specified)
+\newenvironment{DoxyInlineImage}{%
+}{%
+}
+
 % Used by @attention
 \newenvironment{DoxyAttention}[1]{%
   \begin{DoxyDesc}{#1}%
-- 
cgit v0.12

