diff --git a/cal_tools/cal_tools/tools.py b/cal_tools/cal_tools/tools.py
index 16af777d056699ff0df1f771f1b38c54e8c03921..2e8961c002d7685df06ef97a0671f0ad6db4ece2 100644
--- a/cal_tools/cal_tools/tools.py
+++ b/cal_tools/cal_tools/tools.py
@@ -502,3 +502,30 @@ def get_constant_from_db(device, constant, condition, empty_constant,
                                int(print_once), timeout)
 
     return data
+
+
+def tex_escape(text):
+    """
+    Escape latex special characters found in the text
+
+    :param text: a plain text message
+    :return: the message escaped to appear correctly in LaTeX
+    """
+    conv = {
+        '&': r'\&',
+        '%': r'\%',
+        '$': r'\$',
+        '#': r'\#',
+        '_': r'\_',
+        '{': r'\{',
+        '}': r'\}',
+        '~': r'\textasciitilde{}',
+        '^': r'\^{}',
+        '\\': r'\textbackslash{}',
+        '<': r'\textless{}',
+        '>': r'\textgreater{}',
+    }
+
+    key_list = sorted(conv.keys(), key=lambda item: - len(item))
+    regex = re.compile('|'.join(re.escape(str(key)) for key in key_list))
+    return regex.sub(lambda match: conv[match.group()], text)
diff --git a/xfel_calibrate/calibrate.py b/xfel_calibrate/calibrate.py
index 0579510e965f6c7f8b130eea2f9267f3548c6fda..d6609ec89112e2156806864ee5cc6557813edfbf 100755
--- a/xfel_calibrate/calibrate.py
+++ b/xfel_calibrate/calibrate.py
@@ -20,6 +20,8 @@ from .notebooks import notebooks
 from jinja2 import Template
 import textwrap
 
+from cal_tools.tools import tex_escape
+
 # Add a class combining raw description formatting with
 # Metavariable default outputs
 class RawTypeFormatter(argparse.RawDescriptionHelpFormatter,
@@ -487,10 +489,17 @@ def make_par_table(parms, temp_path, run_uuid):
     :param run_uuid: inset of folder name containing job output
     """
 
-    # Add space in long strings to wrap them in latex
+    # Add space in long strings without line breakers ` ,-` to
+    # wrap them in latex
     def split_len(seq, length):
-        l = [seq[i:i + length] for i in range(0, len(seq), length)]
-        return ' '.join(l)
+        lbc = set(' ,-')
+        line = ''
+        for i in range(0, len(seq), length):
+            sub_line = seq[i:i + length]
+            line += sub_line
+            if not any(c in lbc for c in sub_line):
+                line += ' '
+        return line
 
     # Prepare strings and estimate their length
     l_parms = []
@@ -508,8 +517,8 @@ def make_par_table(parms, temp_path, run_uuid):
             value = split_len(value, max_len[1])
         if p.type is str:
             value = "``{}''".format(value)
-        value = value.replace('_', '\\_')
-        comment = str(p.comment)[1:].replace('_', '\\_')
+        value = tex_escape(value)
+        comment = tex_escape(str(p.comment)[1:])
         l_parms.append([name, value, comment])
 
     # Fix column width is needed
@@ -525,7 +534,7 @@ def make_par_table(parms, temp_path, run_uuid):
                     
                     .. math::
                     
-                        \\begin{tabular}{ {% for k in p %}{{ k }}{%- endfor %}  } 
+                        \\begin{tabular}{ {% for k in p %}{{k}}{%- endfor %} } 
                         \hline
                         {% for k in lines %}
                         {{ k[0] }} & {{ k[1] }} & {{ k[2] }} \\\\