From 8764c3da9530d47c009e5d014b921b5369d6e819 Mon Sep 17 00:00:00 2001 From: Timo Heister Date: Fri, 3 Nov 2017 12:35:43 -0400 Subject: [PATCH] escape characters in LaTeX format of ParameterHandler - escape special latex characters (_, \, %, ...) - mangle label names - document more details about print_parameters() --- .../incompatibilities/20171103TimoHeister | 4 + include/deal.II/base/parameter_handler.h | 16 +++ include/deal.II/base/patterns.h | 8 ++ source/base/parameter_handler.cc | 99 ++++++++++++------- source/base/patterns.cc | 70 +++++++++++-- tests/parameter_handler/write_latex_01.cc | 68 +++++++++++++ tests/parameter_handler/write_latex_01.output | 93 +++++++++++++++++ 7 files changed, 315 insertions(+), 43 deletions(-) create mode 100644 doc/news/changes/incompatibilities/20171103TimoHeister create mode 100644 tests/parameter_handler/write_latex_01.cc create mode 100644 tests/parameter_handler/write_latex_01.output diff --git a/doc/news/changes/incompatibilities/20171103TimoHeister b/doc/news/changes/incompatibilities/20171103TimoHeister new file mode 100644 index 0000000000..258dc88021 --- /dev/null +++ b/doc/news/changes/incompatibilities/20171103TimoHeister @@ -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. +
+(Timo Heister, 2017/11/03) diff --git a/include/deal.II/base/parameter_handler.h b/include/deal.II/base/parameter_handler.h index e89dd053d6..d18afa83db 100644 --- a/include/deal.II/base/parameter_handler.h +++ b/include/deal.II/base/parameter_handler.h @@ -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 parameters:section1/subsection1 + * and parameters:section1/subsection1/someentry. Because + * special characters can appear in the section and entry names, these + * will be "mangled". Here, all characters except [a-zA-Z0-9] + * are replaced by _XX, where XX is the digit + * ascii code of the character in hexadecimal encoding (so a space becomes + * _20 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 @\index * statements in two indices called prmindex (where the name of * each parameter is listed in the index) and prmindexfull diff --git a/include/deal.II/base/patterns.h b/include/deal.II/base/patterns.h index 4e75010678..8b01b979f5 100644 --- a/include/deal.II/base/patterns.h +++ b/include/deal.II/base/patterns.h @@ -148,6 +148,14 @@ namespace Patterns */ std::unique_ptr 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 diff --git a/source/base/parameter_handler.cc b/source/base/parameter_handler.cc index ff220d0ca0..8799e35939 100644 --- a/source/base/parameter_handler.cc +++ b/source/base/parameter_handler.cc @@ -1104,6 +1104,11 @@ ParameterHandler::recursively_print_parameters (const std::vector & 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 & const std::string value = p->second.get("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; ifirst); + 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; ifirst) + 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("default_value") << "\n\n" + << escape(p->second.get("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("documentation").empty()) out << "{\\it Description:} " << p->second.get("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 ("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 & const std::string alias = p->second.get("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; ifirst); + 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; ifirst) + 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("deprecation_status") == "true" ? " Its use is deprecated." @@ -1313,14 +1325,19 @@ ParameterHandler::recursively_print_parameters (const std::vector & 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; ifirst); + 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("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; ifirst); + 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; ifirst) + 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("default_value") << "\n\n" + << escape(p->second.get("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("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; ifirst); + 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; ifirst) + 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("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; ifirst); + out << escape(subsection_path[i]) << "/"; + out << escape(demangle(p->first)); out << "}" << std::endl; out << "\\label{parameters:"; diff --git a/source/base/patterns.cc b/source/base/patterns.cc index e53923c656..e146091d01 100644 --- a/source/base/patterns.cc +++ b/source/base/patterns.cc @@ -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 pattern_factory(const std::string &description) { std::unique_ptr 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; idescription(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 index 0000000000..7a28576e84 --- /dev/null +++ b/tests/parameter_handler/write_latex_01.cc @@ -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 + + +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 index 0000000000..8c192f5d6c --- /dev/null +++ b/tests/parameter_handler/write_latex_01.output @@ -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} -- 2.39.5