]> https://gitweb.dealii.org/ - dealii.git/commitdiff
escape characters in LaTeX format of ParameterHandler
authorTimo Heister <timo.heister@gmail.com>
Fri, 3 Nov 2017 16:35:43 +0000 (12:35 -0400)
committerTimo Heister <timo.heister@gmail.com>
Fri, 3 Nov 2017 16:35:43 +0000 (12:35 -0400)
- escape special latex characters (_, \, %, ...)
- mangle label names
- document more details about print_parameters()

doc/news/changes/incompatibilities/20171103TimoHeister [new file with mode: 0644]
include/deal.II/base/parameter_handler.h
include/deal.II/base/patterns.h
source/base/parameter_handler.cc
source/base/patterns.cc
tests/parameter_handler/write_latex_01.cc [new file with mode: 0644]
tests/parameter_handler/write_latex_01.output [new file with mode: 0644]

diff --git a/doc/news/changes/incompatibilities/20171103TimoHeister b/doc/news/changes/incompatibilities/20171103TimoHeister
new file mode 100644 (file)
index 0000000..258dc88
--- /dev/null
@@ -0,0 +1,4 @@
+Changed: ParameterHandler::print_parameters() in LaTeX format now correctly
+escapes special characters. As a consequence, the names of labels changed because they are now mangled. See the documentation for more details.
+<br>
+(Timo Heister, 2017/11/03)
index e89dd053d6880a950ea776ec515745f704271271..d18afa83dbff5285691730a7267aa7c1a4cd44c3 100644 (file)
@@ -1227,6 +1227,22 @@ public:
    * parameters. The various sections of parameters are then represented by
    * latex section and subsection commands as well as by nested enumerations.
    *
+   * You can reference specific parameter sections and individual parameters
+   * by the labels that are generated automatically for each entry. The
+   * labels have the format <code>parameters:section1/subsection1</code>
+   * and <code>parameters:section1/subsection1/someentry</code>. Because
+   * special characters can appear in the section and entry names, these
+   * will be "mangled". Here, all characters except <code>[a-zA-Z0-9]</code>
+   * are replaced by <code>_XX</code>, where <code>XX</code> is the digit
+   * ascii code of the character in hexadecimal encoding (so a space becomes
+   * <code>_20</code> for example).
+   *
+   * While this function escapes special LaTeX-specific characters (backslash,
+   * underscore, etc.) in most of the output (names, default values, etc.),
+   * the documentation string is passed as-is. This means you can use math
+   * environments and other formatting in the description, but you need
+   * to escape quotes, backslashes, underscores, etc. yourself.
+   *
    * In addition, all parameter names are listed with <code>@\index</code>
    * statements in two indices called <code>prmindex</code> (where the name of
    * each parameter is listed in the index) and <code>prmindexfull</code>
index 4e75010678b4543bb5084117712aabe2b2d88e02..8b01b979f56faf1a4ef9b329e61c8ec567705eb7 100644 (file)
@@ -148,6 +148,14 @@ namespace Patterns
    */
   std::unique_ptr<PatternBase> pattern_factory (const std::string &description);
 
+  /**
+   * Escape the string @p input for the specified @p style so that characters
+   * will appear as intended. For example, characters like _ can not be
+   * written as is in LateX and have to be escaped as \_.
+   */
+  std::string escape(const std::string &input, const PatternBase::OutputStyle style);
+
+
   /**
    * Test for the string being an integer. If bounds are given to the
    * constructor, then the integer given also needs to be within the interval
index ff220d0ca0981448b6c0e3d61539cb130ec139e5..8799e3593907429e0206b5d6656b099bc45161cf 100644 (file)
@@ -1104,6 +1104,11 @@ ParameterHandler::recursively_print_parameters (const std::vector<std::string> &
 
     case LaTeX:
     {
+      auto escape = [] (const std::string &input)
+      {
+        return Patterns::escape(input, Patterns::PatternBase::LaTeX);
+      };
+
       // if there are any parameters in this section then print them
       // as an itemized list
       bool parameters_exist_here = false;
@@ -1133,37 +1138,42 @@ ParameterHandler::recursively_print_parameters (const std::vector<std::string> &
                 const std::string value = p->second.get<std::string>("value");
 
                 // print name
-                out << "\\item {\\it Parameter name:} {\\tt " << demangle(p->first) << "}\n"
+                out << "\\item {\\it Parameter name:} {\\tt "
+                    << escape(demangle(p->first)) << "}\n"
                     << "\\phantomsection\\label{parameters:";
+                // labels are not to be escaped but mangled:
                 for (unsigned int i=0; i<target_subsection_path.size(); ++i)
-                  out << target_subsection_path[i] << "/";
-                out << demangle(p->first);
+                  out << mangle(target_subsection_path[i]) << "/";
+                out << p->first;
                 out << "}\n\n"
                     << '\n';
 
                 out << "\\index[prmindex]{"
-                    << demangle(p->first)
+                    << escape(demangle(p->first))
                     << "}\n";
                 out << "\\index[prmindexfull]{";
                 for (unsigned int i=0; i<target_subsection_path.size(); ++i)
-                  out << target_subsection_path[i] << "!";
-                out << demangle(p->first)
+                  out << escape(target_subsection_path[i]) << "!";
+                out << escape(demangle(p->first))
                     << "}\n";
 
                 // finally print value and default
-                out << "{\\it Value:} " << value << "\n\n"
+                out << "{\\it Value:} " << escape(value) << "\n\n"
                     << '\n'
                     << "{\\it Default:} "
-                    << p->second.get<std::string>("default_value") << "\n\n"
+                    << escape(p->second.get<std::string>("default_value"))
+                    << "\n\n"
                     << '\n';
 
-                // if there is a documenting string, print it as well
+                // if there is a documenting string, print it as well but
+                // don't escape to allow formatting/formulas
                 if (!p->second.get<std::string>("documentation").empty())
                   out << "{\\it Description:} "
                       << p->second.get<std::string>("documentation") << "\n\n"
                       << '\n';
 
-                // also output possible values
+                // also output possible values, do not escape because the
+                // description internally will use LaTeX formatting
                 const unsigned int pattern_index = p->second.get<unsigned int> ("pattern");
                 const std::string desc_str = patterns[pattern_index]->description (Patterns::PatternBase::LaTeX);
                 out << "{\\it Possible values:} "
@@ -1175,26 +1185,28 @@ ParameterHandler::recursively_print_parameters (const std::vector<std::string> &
                 const std::string alias = p->second.get<std::string>("alias");
 
                 // print name
-                out << "\\item {\\it Parameter name:} {\\tt " << demangle(p->first) << "}\n"
+                out << "\\item {\\it Parameter name:} {\\tt "
+                    << escape(demangle(p->first)) << "}\n"
                     << "\\phantomsection\\label{parameters:";
+                // do not escape but mangle labels:
                 for (unsigned int i=0; i<target_subsection_path.size(); ++i)
-                  out << target_subsection_path[i] << "/";
-                out << demangle(p->first);
+                  out << mangle(target_subsection_path[i]) << "/";
+                out << p->first;
                 out << "}\n\n"
                     << '\n';
 
                 out << "\\index[prmindex]{"
-                    << demangle(p->first)
+                    << escape(demangle(p->first))
                     << "}\n";
                 out << "\\index[prmindexfull]{";
                 for (unsigned int i=0; i<target_subsection_path.size(); ++i)
-                  out << target_subsection_path[i] << "!";
-                out << demangle(p->first)
+                  out << escape(target_subsection_path[i]) << "!";
+                out << escape(demangle(p->first))
                     << "}\n";
 
                 // finally print alias and indicate if it is deprecated
                 out << "This parameter is an alias for the parameter ``\\texttt{"
-                    << alias << "}''."
+                    << escape(alias) << "}''."
                     << (p->second.get<std::string>("deprecation_status") == "true"
                         ?
                         " Its use is deprecated."
@@ -1313,14 +1325,19 @@ ParameterHandler::recursively_print_parameters (const std::vector<std::string> &
 
           case LaTeX:
           {
+            auto escape = [] (const std::string &input)
+            {
+              return Patterns::escape(input, Patterns::PatternBase::LaTeX);
+            };
+
             out << '\n'
                 << "\\subsection{Parameters in section \\tt ";
 
             // find the path to the current section so that we can
             // print it in the \subsection{...} heading
             for (unsigned int i=0; i<target_subsection_path.size(); ++i)
-              out << target_subsection_path[i] << "/";
-            out << demangle(p->first);
+              out << escape(target_subsection_path[i]) << "/";
+            out << escape(demangle(p->first));
 
             out << "}" << '\n';
             out << "\\label{parameters:";
@@ -1538,6 +1555,11 @@ ParameterHandler::print_parameters_section (std::ostream      &out,
 
     case LaTeX:
     {
+      auto escape = [] (const std::string &input)
+      {
+        return Patterns::escape(input, Patterns::PatternBase::LaTeX);
+      };
+
       // if there are any parameters in this section then print them
       // as an itemized list
       bool parameters_exist_here = false;
@@ -1567,28 +1589,28 @@ ParameterHandler::print_parameters_section (std::ostream      &out,
                 const std::string value = p->second.get<std::string>("value");
 
                 // print name
-                out << "\\item {\\it Parameter name:} {\\tt " << demangle(p->first) << "}\n"
+                out << "\\item {\\it Parameter name:} {\\tt " << escape(demangle(p->first)) << "}\n"
                     << "\\phantomsection\\label{parameters:";
                 for (unsigned int i=0; i<subsection_path.size(); ++i)
-                  out << subsection_path[i] << "/";
-                out << demangle(p->first);
+                  out << mangle(subsection_path[i]) << "/";
+                out << p->first;
                 out << "}\n\n"
                     << std::endl;
 
                 out << "\\index[prmindex]{"
-                    << demangle(p->first)
+                    << escape(demangle(p->first))
                     << "}\n";
                 out << "\\index[prmindexfull]{";
                 for (unsigned int i=0; i<subsection_path.size(); ++i)
-                  out << subsection_path[i] << "!";
-                out << demangle(p->first)
+                  out << escape(subsection_path[i]) << "!";
+                out << escape(demangle(p->first))
                     << "}\n";
 
                 // finally print value and default
-                out << "{\\it Value:} " << value << "\n\n"
+                out << "{\\it Value:} " << escape(value) << "\n\n"
                     << std::endl
                     << "{\\it Default:} "
-                    << p->second.get<std::string>("default_value") << "\n\n"
+                    << escape(p->second.get<std::string>("default_value")) << "\n\n"
                     << std::endl;
 
                 // if there is a documenting string, print it as well
@@ -1609,26 +1631,26 @@ ParameterHandler::print_parameters_section (std::ostream      &out,
                 const std::string alias = p->second.get<std::string>("alias");
 
                 // print name
-                out << "\\item {\\it Parameter name:} {\\tt " << demangle(p->first) << "}\n"
+                out << "\\item {\\it Parameter name:} {\\tt " << escape(demangle(p->first)) << "}\n"
                     << "\\phantomsection\\label{parameters:";
                 for (unsigned int i=0; i<subsection_path.size(); ++i)
-                  out << subsection_path[i] << "/";
-                out << demangle(p->first);
+                  out << mangle(subsection_path[i]) << "/";
+                out << p->first;
                 out << "}\n\n"
                     << std::endl;
 
                 out << "\\index[prmindex]{"
-                    << demangle(p->first)
+                    << escape(demangle(p->first))
                     << "}\n";
                 out << "\\index[prmindexfull]{";
                 for (unsigned int i=0; i<subsection_path.size(); ++i)
-                  out << subsection_path[i] << "!";
-                out << demangle(p->first)
+                  out << escape(subsection_path[i]) << "!";
+                out << escape(demangle(p->first))
                     << "}\n";
 
                 // finally print alias and indicate if it is deprecated
                 out << "This parameter is an alias for the parameter ``\\texttt{"
-                    << alias << "}''."
+                    << escape(alias) << "}''."
                     << (p->second.get<std::string>("deprecation_status") == "true"
                         ?
                         " Its use is deprecated."
@@ -1752,14 +1774,19 @@ ParameterHandler::print_parameters_section (std::ostream      &out,
                 break;
               case LaTeX:
               {
+                auto escape = [] (const std::string &input)
+                {
+                  return Patterns::escape(input, Patterns::PatternBase::LaTeX);
+                };
+
                 out << std::endl
                     << "\\subsection{Parameters in section \\tt ";
 
                 // find the path to the current section so that we can
                 // print it in the \subsection{...} heading
                 for (unsigned int i=0; i<subsection_path.size(); ++i)
-                  out << subsection_path[i] << "/";
-                out << demangle(p->first);
+                  out << escape(subsection_path[i]) << "/";
+                out << escape(demangle(p->first));
 
                 out << "}" << std::endl;
                 out << "\\label{parameters:";
index e53923c656f84a7570eee2db0600c5380e9b3bbd..e146091d019f6e1127e23928c5a5c3de3da03c4f 100644 (file)
@@ -76,6 +76,60 @@ namespace Patterns
 
 
 
+  std::string escape(const std::string &input, const PatternBase::OutputStyle style)
+  {
+    switch (style)
+      {
+      case PatternBase::Machine:
+      case PatternBase::Text:
+        return input;
+      case PatternBase::LaTeX:
+      {
+        std::string u;
+        u.reserve (input.size());
+        for (auto c : input)
+          {
+            switch (c)
+              {
+              case '#':
+              case '$':
+              case '%':
+              case '&':
+              case '_':
+              case '{':
+              case '}':
+                // simple escaping:
+                u.push_back('\\');
+                u.push_back(c);
+                break;
+
+              case '\\':
+                u.append("\\textbackslash{}");
+                break;
+
+              case '^':
+                u.append("\\^{}");
+                break;
+
+              case '~':
+                u.append("\\~{}");
+                break;
+
+              default:
+                // all other chars are just copied:
+                u.push_back(c);
+              }
+          }
+        return u;
+      }
+      default:
+        Assert(false, ExcNotImplemented());
+      }
+    return "";
+  }
+
+
+
   std::unique_ptr<PatternBase> pattern_factory(const std::string &description)
   {
     std::unique_ptr<PatternBase> p;
@@ -517,7 +571,7 @@ namespace Patterns
         std::ostringstream description;
 
         description << "Any one of "
-                    << Utilities::replace_in_string(sequence,"|",", ");
+                    << escape(Utilities::replace_in_string(sequence,"|",", "), style);
 
         return description.str();
       }
@@ -656,7 +710,9 @@ namespace Patterns
                     << min_elements << " to " << max_elements
                     << " elements ";
         if (separator != ",")
-          description << "separated by <" << separator << "> ";
+          description << "separated by <"
+                      << escape(separator, style)
+                      << "> ";
         description  << "where each element is ["
                      << pattern->description(style)
                      << "]";
@@ -828,12 +884,12 @@ namespace Patterns
         std::ostringstream description;
 
         description << "A key"
-                    << key_value_separator
+                    << escape(key_value_separator, style)
                     << "value map of "
                     << min_elements << " to " << max_elements
                     << " elements ";
         if (separator != ",")
-          description << " separated by <" << separator << "> ";
+          description << " separated by <" << escape(separator, style) << "> ";
         description << " where each key is ["
                     << key_pattern->description(style)
                     << "]"
@@ -1035,13 +1091,13 @@ namespace Patterns
                     << patterns.size()
                     << " elements ";
         if (separator != ":")
-          description << " separated by <" << separator << "> ";
+          description << " separated by <" << escape(separator, style) << "> ";
         description << " where each element is ["
                     <<  patterns[0]->description(style)
                     << "]";
         for (unsigned int i=1; i<patterns.size(); ++i)
           {
-            description << separator
+            description << escape(separator, style)
                         << "[" << patterns[i]->description(style) << "]";
 
           }
@@ -1234,7 +1290,7 @@ namespace Patterns
         std::ostringstream description;
 
         description << "A comma-separated list of any of "
-                    << Utilities::replace_in_string(sequence,"|",", ");
+                    << escape(Utilities::replace_in_string(sequence,"|",", "), style);
 
         return description.str();
       }
diff --git a/tests/parameter_handler/write_latex_01.cc b/tests/parameter_handler/write_latex_01.cc
new file mode 100644 (file)
index 0000000..7a28576
--- /dev/null
@@ -0,0 +1,68 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2003 - 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+
+
+// test escaping of underscores in ParameterHandler::print_parameters(LaTeX)
+
+#include "../tests.h"
+#include <deal.II/base/parameter_handler.h>
+
+
+int main ()
+{
+  initlog();
+
+  // latex header to make it easy to check the .tex output:
+  deallog.get_file_stream() << "\\documentclass{article}\n"
+                            "\\usepackage{amsmath}\n"
+                            "\\usepackage{imakeidx}\n"
+                            "\\makeindex[name=prmindex, title=Index of run-time parameter entries]\n"
+                            "\\makeindex[name=prmindexfull, title=Index of run-time parameters with section names]\n"
+                            "\\usepackage[colorlinks,linkcolor=blue,urlcolor=blue,citecolor=blue,baseurl=../]{hyperref}\n"
+                            "\\begin{document}\n" << std::endl;
+
+  ParameterHandler prm;
+  prm.enter_subsection ("section_1");
+  {
+    prm.enter_subsection ("section_2&3");
+    {
+      prm.declare_entry ("list",
+                         "default_1",
+                         Patterns::List(Patterns::Selection("default_1|b|c|d|e|f|g|h")),
+                         "docs 1");
+      prm.declare_entry ("int_hello",
+                         "1",
+                         Patterns::Integer());
+      prm.declare_entry ("text",
+                         "default text with _ ~ \\ # & { }",
+                         Patterns::Anything(),
+                         "documentation with formulas: $x_3$");
+    }
+    prm.leave_subsection ();
+    prm.declare_entry ("some other entry with {%}",
+                       "3.1415926",
+                       Patterns::Double(),
+                       "documentation");
+  }
+  prm.leave_subsection ();
+
+  prm.print_parameters (deallog.get_file_stream(), ParameterHandler::LaTeX);
+
+  deallog.get_file_stream() << "\n\n"
+                            "\\printindex[prmindex]\n"
+                            "\\printindex[prmindexfull]\n"
+                            "\\end{document}" << std::endl;
+}
diff --git a/tests/parameter_handler/write_latex_01.output b/tests/parameter_handler/write_latex_01.output
new file mode 100644 (file)
index 0000000..8c192f5
--- /dev/null
@@ -0,0 +1,93 @@
+
+\documentclass{article}
+\usepackage{amsmath}
+\usepackage{imakeidx}
+\makeindex[name=prmindex, title=Index of run-time parameter entries]
+\makeindex[name=prmindexfull, title=Index of run-time parameters with section names]
+\usepackage[colorlinks,linkcolor=blue,urlcolor=blue,citecolor=blue,baseurl=../]{hyperref}
+\begin{document}
+
+\subsection{Global parameters}
+\label{parameters:global}
+
+
+
+\subsection{Parameters in section \tt section\_1}
+\label{parameters:section_5f1}
+
+\begin{itemize}
+\item {\it Parameter name:} {\tt some other entry with \{\%\}}
+\phantomsection\label{parameters:section_5f1/some_20other_20entry_20with_20_7b_25_7d}
+
+
+\index[prmindex]{some other entry with \{\%\}}
+\index[prmindexfull]{section\_1!some other entry with \{\%\}}
+{\it Value:} 3.1415926
+
+
+{\it Default:} 3.1415926
+
+
+{\it Description:} documentation
+
+
+{\it Possible values:} A floating point number $v$ such that $-\text{MAX\_DOUBLE} \leq v \leq \text{MAX\_DOUBLE}$
+\end{itemize}
+
+
+
+\subsection{Parameters in section \tt section\_1/section\_2\&3}
+\label{parameters:section_5f1/section_5f2_263}
+
+\begin{itemize}
+\item {\it Parameter name:} {\tt int\_hello}
+\phantomsection\label{parameters:section_5f1/section_5f2_263/int_5fhello}
+
+
+\index[prmindex]{int\_hello}
+\index[prmindexfull]{section\_1!section\_2\&3!int\_hello}
+{\it Value:} 1
+
+
+{\it Default:} 1
+
+
+{\it Possible values:} An integer $n$ such that $-2147483648\leq n \leq 2147483647$
+\item {\it Parameter name:} {\tt list}
+\phantomsection\label{parameters:section_5f1/section_5f2_263/list}
+
+
+\index[prmindex]{list}
+\index[prmindexfull]{section\_1!section\_2\&3!list}
+{\it Value:} default\_1
+
+
+{\it Default:} default\_1
+
+
+{\it Description:} docs 1
+
+
+{\it Possible values:} A list of 0 to 4294967295 elements where each element is [Any one of default\_1, b, c, d, e, f, g, h]
+\item {\it Parameter name:} {\tt text}
+\phantomsection\label{parameters:section_5f1/section_5f2_263/text}
+
+
+\index[prmindex]{text}
+\index[prmindexfull]{section\_1!section\_2\&3!text}
+{\it Value:} default text with \_ \~{} \textbackslash{} \# \& \{ \}
+
+
+{\it Default:} default text with \_ \~{} \textbackslash{} \# \& \{ \}
+
+
+{\it Description:} documentation with formulas: $x_3$
+
+
+{\it Possible values:} Any string
+\end{itemize}
+
+
+\printindex[prmindex]
+\printindex[prmindexfull]
+\end{document}

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.