Imported Upstream version 1.9.8
[platform/upstream/doxygen.git] / src / latexdocvisitor.cpp
index feb4f93..451c189 100644 (file)
@@ -1,9 +1,6 @@
 /******************************************************************************
  *
- *
- *
- *
- * Copyright (C) 1997-2015 by Dimitri van Heesch.
+ * Copyright (C) 1997-2022 by Dimitri van Heesch.
  *
  * Permission to use, copy, modify, and distribute this software and its
  * documentation under the terms of the GNU General Public License is hereby
@@ -17,6 +14,7 @@
  */
 
 #include <algorithm>
+#include <array>
 
 #include "htmlattrib.h"
 #include "latexdocvisitor.h"
@@ -25,6 +23,7 @@
 #include "language.h"
 #include "doxygen.h"
 #include "outputgen.h"
+#include "outputlist.h"
 #include "dot.h"
 #include "util.h"
 #include "message.h"
 #include "plantuml.h"
 #include "fileinfo.h"
 #include "regex.h"
+#include "portable.h"
 
-const int maxLevels=5;
-static const char *secLabels[maxLevels] =
-   { "doxysection","doxysubsection","doxysubsubsection","doxyparagraph","doxysubparagraph" };
+static const int g_maxLevels = 7;
+static const std::array<const char *,g_maxLevels> g_secLabels =
+{ "doxysection",
+  "doxysubsection",
+  "doxysubsubsection",
+  "doxysubsubsubsection",
+  "doxysubsubsubsubsection",
+  "doxysubsubsubsubsubsection",
+  "doxysubsubsubsubsubsubsection"
+};
 
-static const char *getSectionName(int level)
+static const char *g_paragraphLabel = "doxyparagraph";
+static const char *g_subparagraphLabel = "doxysubparagraph";
+
+const char *LatexDocVisitor::getSectionName(int level) const
 {
   bool compactLatex = Config_getBool(COMPACT_LATEX);
   int l = level;
   if (compactLatex) l++;
-  if (Doxygen::insideMainPage) l--;
-  return secLabels[std::min(maxLevels-1,l)];
+
+  if (l <= 3)
+  {
+    // Sections get special treatment because they inherit the parent's level
+    l += m_hierarchyLevel; /* May be -1 if generating main page */
+    if (l >= g_maxLevels)
+    {
+      l = g_maxLevels - 1;
+    }
+    else if (l < 0)
+    {
+      /* Should not happen; level is always >= 1 and hierarchyLevel >= -1 */
+      l = 0;
+    }
+    return g_secLabels[l];
+  }
+  else if (l == 4)
+  {
+    return g_paragraphLabel;
+  }
+  else
+  {
+    return g_subparagraphLabel;
+  }
 }
 
 static void insertDimension(TextStream &t, QCString dimension, const char *orientationString)
@@ -201,11 +233,11 @@ QCString LatexDocVisitor::escapeMakeIndexChars(const char *s)
 }
 
 
-LatexDocVisitor::LatexDocVisitor(TextStream &t,LatexCodeGenerator &ci,
-                                 const QCString &langExt,bool insideTabbing)
-  : m_t(t), m_ci(ci), m_insidePre(FALSE),
+LatexDocVisitor::LatexDocVisitor(TextStream &t,OutputCodeList &ci,LatexCodeGenerator &lcg,
+                                 const QCString &langExt, int hierarchyLevel)
+  : m_t(t), m_ci(ci), m_lcg(lcg), m_insidePre(FALSE),
     m_insideItem(FALSE), m_hide(FALSE),
-    m_insideTabbing(insideTabbing), m_langExt(langExt)
+    m_langExt(langExt), m_hierarchyLevel(hierarchyLevel)
 {
 }
 
@@ -244,7 +276,7 @@ void LatexDocVisitor::operator()(const DocSymbol &s)
 {
   if (m_hide) return;
   bool pdfHyperlinks = Config_getBool(PDF_HYPERLINKS);
-  const char *res = HtmlEntityMapper::instance()->latex(s.symbol());
+  const char *res = HtmlEntityMapper::instance().latex(s.symbol());
   if (res)
   {
     if (((s.symbol() == HtmlEntityMapper::Sym_lt) || (s.symbol() == HtmlEntityMapper::Sym_Less))&& (!m_insidePre))
@@ -276,14 +308,14 @@ void LatexDocVisitor::operator()(const DocSymbol &s)
   }
   else
   {
-    err("LaTeX: non supported HTML-entity found: %s\n",HtmlEntityMapper::instance()->html(s.symbol(),TRUE));
+    err("LaTeX: non supported HTML-entity found: %s\n",HtmlEntityMapper::instance().html(s.symbol(),TRUE));
   }
 }
 
 void LatexDocVisitor::operator()(const DocEmoji &s)
 {
   if (m_hide) return;
-  QCString emojiName = EmojiEntityMapper::instance()->name(s.index());
+  QCString emojiName = EmojiEntityMapper::instance().name(s.index());
   if (!emojiName.isEmpty())
   {
     QCString imageName=emojiName.mid(1,emojiName.length()-2); // strip : at start and end
@@ -378,9 +410,6 @@ void LatexDocVisitor::operator()(const DocStyleChange &s)
       break;
     case DocStyleChange::Div:  /* HTML only */ break;
     case DocStyleChange::Span: /* HTML only */ break;
-    case DocStyleChange::Summary: /* emulation of the <summary> tag inside a <details> tag */
-      if (s.enable()) m_t << "{\\bfseries{";      else m_t << "}}\\newline";
-      break;
   }
 }
 
@@ -436,7 +465,7 @@ void LatexDocVisitor::operator()(const DocVerbatim &s)
             dotindex++,
             ".dot"
            );
-        std::ofstream file(fileName.str(),std::ofstream::out | std::ofstream::binary);
+        std::ofstream file = Portable::openOutputStream(fileName);
         if (!file.is_open())
         {
           err("Could not open file %s for writing\n",qPrint(fileName));
@@ -463,11 +492,11 @@ void LatexDocVisitor::operator()(const DocVerbatim &s)
             qPrint(Config_getString(LATEX_OUTPUT)+"/inline_mscgraph_"),
             mscindex++
            );
-        std::string fileName = baseName.str()+".msc";
-        std::ofstream file(fileName,std::ofstream::out | std::ofstream::binary);
+        QCString fileName = baseName+".msc";
+        std::ofstream file = Portable::openOutputStream(fileName);
         if (!file.is_open())
         {
-          err("Could not open file %s for writing\n",fileName.c_str());
+          err("Could not open file %s for writing\n",qPrint(fileName));
         }
         else
         {
@@ -479,7 +508,7 @@ void LatexDocVisitor::operator()(const DocVerbatim &s)
 
           writeMscFile(baseName, s);
 
-          if (Config_getBool(DOT_CLEANUP)) Dir().remove(fileName);
+          if (Config_getBool(DOT_CLEANUP)) Dir().remove(fileName.str());
         }
       }
       break;
@@ -518,20 +547,19 @@ void LatexDocVisitor::operator()(const DocInclude &inc)
       {
         m_ci.startCodeFragment("DoxyCodeInclude");
         FileInfo cfi( inc.file().str() );
-        FileDef *fd = createFileDef( cfi.dirPath(), cfi.fileName() );
+        auto fd = createFileDef( cfi.dirPath(), cfi.fileName() );
         getCodeParser(inc.extension()).parseCode(m_ci,inc.context(),
                                                   inc.text(),
                                                   langExt,
                                                   inc.isExample(),
                                                   inc.exampleFile(),
-                                                  fd,    // fileDef,
+                                                  fd.get(),    // fileDef,
                                                   -1,    // start line
                                                   -1,    // end line
                                                   FALSE, // inline fragment
                                                   0,     // memberDef
                                                   TRUE   // show line numbers
                                                         );
-        delete fd;
         m_ci.endCodeFragment("DoxyCodeInclude");
       }
       break;
@@ -568,11 +596,12 @@ void LatexDocVisitor::operator()(const DocInclude &inc)
       m_t << "\\end{DoxyVerbInclude}\n";
       break;
     case DocInclude::Snippet:
+    case DocInclude::SnippetTrimLeft:
       {
         m_ci.startCodeFragment("DoxyCodeInclude");
         getCodeParser(inc.extension()).parseCode(m_ci,
                                                   inc.context(),
-                                                  extractBlock(inc.text(),inc.blockId()),
+                                                  extractBlock(inc.text(),inc.blockId(),inc.type()==DocInclude::SnippetTrimLeft),
                                                   langExt,
                                                   inc.isExample(),
                                                   inc.exampleFile()
@@ -583,7 +612,7 @@ void LatexDocVisitor::operator()(const DocInclude &inc)
     case DocInclude::SnipWithLines:
       {
         FileInfo cfi( inc.file().str() );
-        FileDef *fd = createFileDef( cfi.dirPath(), cfi.fileName() );
+        auto fd = createFileDef( cfi.dirPath(), cfi.fileName() );
         m_ci.startCodeFragment("DoxyCodeInclude");
         getCodeParser(inc.extension()).parseCode(m_ci,
                                                   inc.context(),
@@ -591,14 +620,13 @@ void LatexDocVisitor::operator()(const DocInclude &inc)
                                                   langExt,
                                                   inc.isExample(),
                                                   inc.exampleFile(),
-                                                  fd,
+                                                  fd.get(),
                                                   lineBlock(inc.text(),inc.blockId()),
                                                   -1,    // endLine
                                                   FALSE, // inlineFragment
                                                   0,     // memberDef
                                                   TRUE   // show line number
                                                  );
-        delete fd;
         m_ci.endCodeFragment("DoxyCodeInclude");
       }
       break;
@@ -628,7 +656,7 @@ void LatexDocVisitor::operator()(const DocIncOperator &op)
     m_hide = popHidden();
     if (!m_hide)
     {
-      FileDef *fd = 0;
+      std::unique_ptr<FileDef> fd;
       if (!op.includeFileName().isEmpty())
       {
         FileInfo cfi( op.includeFileName().str() );
@@ -637,14 +665,13 @@ void LatexDocVisitor::operator()(const DocIncOperator &op)
 
       getCodeParser(locLangExt).parseCode(m_ci,op.context(),op.text(),langExt,
                                           op.isExample(),op.exampleFile(),
-                                          fd,     // fileDef
+                                          fd.get(),     // fileDef
                                           op.line(),    // startLine
                                           -1,    // endLine
                                           FALSE, // inline fragment
                                           0,     // memberDef
                                           op.showLineNo()  // show line numbers
                                          );
-      if (fd) delete fd;
     }
     pushHidden(m_hide);
     m_hide=TRUE;
@@ -938,12 +965,21 @@ void LatexDocVisitor::operator()(const DocSimpleListItem &li)
 void LatexDocVisitor::operator()(const DocSection &s)
 {
   if (m_hide) return;
-  if (Config_getBool(PDF_HYPERLINKS))
+  bool pdfHyperlinks = Config_getBool(PDF_HYPERLINKS);
+  if (pdfHyperlinks)
   {
     m_t << "\\hypertarget{" << stripPath(s.file()) << "_" << s.anchor() << "}{}";
   }
   m_t << "\\" << getSectionName(s.level()) << "{";
+  if (pdfHyperlinks)
+  {
+    m_t << "\\texorpdfstring{";
+  }
   filter(convertCharEntitiesToUTF8(s.title()));
+  if (pdfHyperlinks)
+  {
+    m_t << "}{" << latexEscapePDFString(convertCharEntitiesToUTF8(s.title())) << "}";
+  }
   m_t << "}\\label{" << stripPath(s.file()) << "_" << s.anchor() << "}\n";
   visitChildren(s);
 }
@@ -1409,12 +1445,33 @@ void LatexDocVisitor::operator()(const DocHRef &href)
   m_t << "}}";
 }
 
+void LatexDocVisitor::operator()(const DocHtmlSummary &d)
+{
+  if (m_hide) return;
+  m_t << "{\\bfseries{";
+  visitChildren(d);
+  m_t << "}}";
+}
+
 void LatexDocVisitor::operator()(const DocHtmlDetails &d)
 {
   if (m_hide) return;
   m_t << "\n\n";
+  auto summary = d.summary();
+  if (summary)
+  {
+    std::visit(*this,*summary);
+    m_t << "\\begin{adjustwidth}{1em}{0em}\n";
+  }
   visitChildren(d);
-  m_t << "\n\n";
+  if (summary)
+  {
+    m_t << "\\end{adjustwidth}\n";
+  }
+  else
+  {
+    m_t << "\n\n";
+  }
 }
 
 void LatexDocVisitor::operator()(const DocHtmlHeader &header)
@@ -1491,7 +1548,7 @@ void LatexDocVisitor::operator()(const DocRef &ref)
   }
   else
   {
-    if (!ref.file().isEmpty()) startLink(ref.ref(),ref.file(),ref.anchor(),ref.refToTable());
+    if (!ref.file().isEmpty()) startLink(ref.ref(),ref.file(),ref.anchor(),ref.refToTable(),ref.refToSection());
   }
   if (!ref.hasLinkText())
   {
@@ -1504,7 +1561,7 @@ void LatexDocVisitor::operator()(const DocRef &ref)
   }
   else
   {
-    if (!ref.file().isEmpty()) endLink(ref.ref(),ref.file(),ref.anchor(),ref.refToTable());
+    if (!ref.file().isEmpty()) endLink(ref.ref(),ref.file(),ref.anchor(),ref.refToTable(),ref.refToSection(),ref.sectionType());
   }
 }
 
@@ -1558,7 +1615,7 @@ void LatexDocVisitor::operator()(const DocParamSect &s)
   if (m_hide) return;
   bool hasInOutSpecs = s.hasInOutSpecifier();
   bool hasTypeSpecs  = s.hasTypeSpecifier();
-  m_ci.incUsedTableLevel();
+  m_lcg.incUsedTableLevel();
   switch(s.type())
   {
     case DocParamSect::Param:
@@ -1586,7 +1643,7 @@ void LatexDocVisitor::operator()(const DocParamSect &s)
   }
   m_t << "}\n";
   visitChildren(s);
-  m_ci.decUsedTableLevel();
+  m_lcg.decUsedTableLevel();
   switch(s.type())
   {
     case DocParamSect::Param:
@@ -1756,17 +1813,18 @@ void LatexDocVisitor::operator()(const DocParBlock &pb)
 
 void LatexDocVisitor::filter(const QCString &str, const bool retainNewLine)
 {
+  //printf("LatexDocVisitor::filter(%s) m_insideTabbing=%d\n",qPrint(str),m_ci.insideTabbing());
   filterLatexString(m_t,str,
-                    m_insideTabbing,
+                    m_lcg.insideTabbing(),
                     m_insidePre,
                     m_insideItem,
-                    m_ci.usedTableLevel()>0,  // insideTable
+                    m_lcg.usedTableLevel()>0,  // insideTable
                     false, // keepSpaces
                     retainNewLine
                    );
 }
 
-void LatexDocVisitor::startLink(const QCString &ref,const QCString &file,const QCString &anchor,bool refToTable)
+void LatexDocVisitor::startLink(const QCString &ref,const QCString &file,const QCString &anchor,bool refToTable,bool refToSection)
 {
   bool pdfHyperLinks = Config_getBool(PDF_HYPERLINKS);
   if (ref.isEmpty() && pdfHyperLinks) // internal PDF link
@@ -1775,15 +1833,23 @@ void LatexDocVisitor::startLink(const QCString &ref,const QCString &file,const Q
     {
       m_t << "\\doxytablelink{";
     }
+    else if (refToSection)
+    {
+      m_t << "\\doxysectlink{";
+    }
     else
     {
-      m_t << "\\mbox{\\hyperlink{";
+      m_t << "\\doxylink{";
     }
     if (!file.isEmpty()) m_t << stripPath(file);
     if (!file.isEmpty() && !anchor.isEmpty()) m_t << "_";
     if (!anchor.isEmpty()) m_t << anchor;
     m_t << "}{";
   }
+  else if (ref.isEmpty() && refToSection)
+  {
+    m_t << "\\doxysectref{";
+  }
   else if (ref.isEmpty() && refToTable)
   {
     m_t << "\\doxytableref{";
@@ -1798,7 +1864,7 @@ void LatexDocVisitor::startLink(const QCString &ref,const QCString &file,const Q
   }
 }
 
-void LatexDocVisitor::endLink(const QCString &ref,const QCString &file,const QCString &anchor,bool refToTable)
+void LatexDocVisitor::endLink(const QCString &ref,const QCString &file,const QCString &anchor,bool /*refToTable*/,bool refToSection, SectionType sectionType)
 {
   m_t << "}";
   bool pdfHyperLinks = Config_getBool(PDF_HYPERLINKS);
@@ -1809,12 +1875,16 @@ void LatexDocVisitor::endLink(const QCString &ref,const QCString &file,const QCS
     m_t << "}{" << file;
     if (!file.isEmpty() && !anchor.isEmpty()) m_t << "_";
     m_t << anchor << "}";
+    if (refToSection)
+    {
+      m_t << "{" << static_cast<int>(sectionType) << "}";
+    }
   }
   if (ref.isEmpty() && pdfHyperLinks) // internal PDF link
   {
-    if (!refToTable)
+    if (refToSection)
     {
-      m_t << "}";
+      m_t << "{" << static_cast<int>(sectionType) << "}";
     }
   }
 }