diff options
author | Matthew Krupcale <mkrupcale@matthewkrupcale.com> | 2019-03-09 21:23:02 -0500 |
---|---|---|
committer | Matthew Krupcale <mkrupcale@matthewkrupcale.com> | 2019-03-14 14:53:02 -0400 |
commit | eb6df5fd19436dfff69c433befca47c47067b909 (patch) | |
tree | a73aa7d20e118678c913d2d08f0c21d2bcbea3ae /cli/context.cxx | |
parent | 7d02451df7a97a03ed010cd324178a666f9ccd7a (diff) | |
download | cli-generate-latex.tar.gz cli-generate-latex.tar.xz cli-generate-latex.zip |
Add support for LaTeX output.generate-latex
* cli/content.cxx: Handle LaTeX generation in format_line and format for escaped and un-escaped CLI translated input.
* cli/context.hxx: Add ot_latex to output_type enum.
* cli/generator.cxx: Include LaTeX output if gen_latex is specified.
* cli/latex.{cxx,hxx}: LaTeX output generation: LaTeX escapes, line-wrapping, and doc, option, class_ traversal
* cli/options.{cli,cxx,hxx,ixx}: Add LaTeX generation options
Diffstat (limited to 'cli/context.cxx')
-rw-r--r-- | cli/context.cxx | 334 |
1 files changed, 330 insertions, 4 deletions
diff --git a/cli/context.cxx b/cli/context.cxx index 3715efa..4d858a6 100644 --- a/cli/context.cxx +++ b/cli/context.cxx @@ -94,6 +94,42 @@ namespace "xor", "xor_eq" }; + + static string + latex_section (map<char, string> const& sm, char t) + { + string s; + typedef map<char, string> map; + map::const_iterator mi (sm.find (t)); + + if (mi == sm.end ()) + { + switch (t) + { + case '0': s = "chapter*"; break; + case '1': s = "chapter"; break; + case 'H': s = "part"; break; + case 'h': s = "section"; break; + case '2': s = "subsection"; break; + } + } + else + s = mi->second; + return s; + } + + static string& + replace_all (string& s, string const& search, string const& replace, + size_t pos = 0) + { + for (; (pos = s.find (search, pos)) != string::npos; + pos += replace.size ()) + { + s.replace (pos, search.size (), replace); + } + return s; + } + } context:: @@ -454,6 +490,11 @@ format_line (output_type ot, string& r, const char* l, size_t n) r += " "; break; } + case ot_latex: + { + r += '~'; + break; + } case ot_man: { r += "\\ "; @@ -471,6 +512,7 @@ format_line (output_type ot, string& r, const char* l, size_t n) switch (ot) { case ot_plain: + case ot_latex: { r += "--"; break; @@ -506,6 +548,14 @@ format_line (output_type ot, string& r, const char* l, size_t n) r += "<br/>"; break; } + case ot_latex: + { + if (!r.empty () && r[r.size () - 1] != '\n') + r += '\n'; + + r += "\\\\"; + break; + } case ot_man: { if (!r.empty () && r[r.size () - 1] != '\n') @@ -528,6 +578,7 @@ format_line (output_type ot, string& r, const char* l, size_t n) { case ot_plain: break; case ot_html: + case ot_latex: case ot_man: { if (i + 1 < n) // More text in this paragraph? @@ -771,6 +822,11 @@ format_line (output_type ot, string& r, const char* l, size_t n) { switch (ot) { + case ot_latex: + { + r += "\\textbackslash{}"; + break; + } case ot_man: { r += "\\e"; @@ -796,7 +852,19 @@ format_line (output_type ot, string& r, const char* l, size_t n) } case '}': { - r += '}'; + switch (ot) + { + case ot_latex: + { + r += "\\}"; + break; + } + default: + { + r += '}'; + break; + } + } break; } case '|': @@ -889,6 +957,67 @@ format_line (output_type ot, string& r, const char* l, size_t n) break; } + case ot_latex: + { + if (s & note) + { + r += "\\footnote{"; + } + else if (s & link) + { + r += '\\'; + + // It might be useful to include the man section into the regex + // somehow. + // + string t (link_section.empty () + ? link_target + : link_target + options.latex_output_suffix ()); + + bool is_internal (true); + + if ( link_target[0] == '#' ) + { + r += "hyperref["; + } + else + { + is_internal = false; + r += "href{"; + if ( link_target.compare(0, 7, "http://") != 0 && + link_target.compare(0, 8, "https://") != 0) + { + r += "run:"; + } + } + + string pt (process_link_target (t)); + + if (pt.empty ()) + { + cerr << "error: link '" << t << "' became empty" << endl; + throw generation_failed (); + } + + if ( is_internal ) + r += string (pt, 1, string::npos) + "]{"; + else + r += pt + "}{"; + } + else + { + if (s & code) + r += "\\texttt{"; + + if (s & itlc) + r += "\\emph{"; + + if (s & bold) + r += "\\textbf{"; + } + + break; + } case ot_man: { if ((s & note) != 0) @@ -931,6 +1060,43 @@ format_line (output_type ot, string& r, const char* l, size_t n) r += '.'; break; } + case '-': + { + if (ot == ot_latex && i + 1 < n && l[i + 1] == '-') + r += "-{}"; + else + r += c; + break; + } + case '^': + { + if (ot == ot_latex) + r += "\\textasciicircum{}"; + else + r += c; + break; + } + case '~': + { + if (ot == ot_latex) + r += "\\textasciitilde{}"; + else + r += c; + break; + } + case '#': + case '$': + case '%': + case '&': + case '_': + case '{': + { + if (ot == ot_latex) + r += string (1, '\\') + c; + else + r += c; + break; + } case '}': { if (!spans.empty ()) @@ -1047,6 +1213,46 @@ format_line (output_type ot, string& r, const char* l, size_t n) break; } + case ot_latex: + { + if ((s & note) != 0) + { + r += "}"; + } + else if ((s & link) != 0) + { + if (link_empty) + { + if (link_section.empty ()) + r += link_target; + else + { + r += "\\texttt{\\textbf{"; + r += link_target + "(" + link_section + ")"; + r += "}}"; + } + } + + r += "}"; + } + else + { + if (s & bold) + r += "}"; + + if (s & itlc) + r += "}"; + + if (s & code) + { + size_t cb (r.rfind("\\texttt{", string::npos, 8)); + replace_all(r, string (1, '\''), string ("\\textquotesingle{}", 18), cb); + r += "}"; + } + } + + break; + } case ot_man: { assert ((s & note) == 0); @@ -1107,6 +1313,11 @@ format_line (output_type ot, string& r, const char* l, size_t n) break; } + else if (ot == ot_latex) + { + r += "\\}"; + break; + } } // Fall through. default: @@ -1226,6 +1437,40 @@ html_margin (string& v) return os.str (); } +// The same idea except there are no margins. So we just strip "\\\\" +// +static void +latex_margin (string& v) +{ + size_t top (0), bot (0); + + const char* b (v.c_str ()); + const char* e (v.c_str () + v.size ()); + + for (; e - b >= 2 && strncmp (b, "\\\\", 2) == 0; ++top) + { + b += 2; + + if (b != e && *b == '\n') // Remove following newline, if any. + ++b; + } + + for (; e - b >= 2 && strncmp (e - 2, "\\\\", 2) == 0; ++bot) + { + e -= 2; + + if (e != b && *(e - 1) == '\n') // Remove preceding newline, if any. + --e; + } + + if (top != 0 || bot != 0) + { + string t; + t.swap (v); + v.assign (b, e - b); + } +} + // The same idea except there are no margins. So we just strip .br. // static void @@ -1337,6 +1582,7 @@ format (semantics::scope& scope, string const& s, bool para) { case ot_plain: break; case ot_html: + case ot_latex: { // Different "newline protocol" inside TOC. // @@ -1531,9 +1777,10 @@ format (semantics::scope& scope, string const& s, bool para) k == block::pre ); break; case block::note: good = (k == block::text || k == block::pre || - (ot == ot_html && (k == block::ul || - k == block::ol || - k == block::dl))); break; + ((ot == ot_html || ot == ot_latex) + && (k == block::ul || + k == block::ol || + k == block::dl))); break; case block::text: good = (k != block::li); break; case block::pre: assert (false); } @@ -1812,6 +2059,26 @@ format (semantics::scope& scope, string const& s, bool para) break; } + case ot_latex: + { + // Separate paragraphs with a blank line. + // + if (!first) + v += "\n\n"; + + if (k == block::pre) + { + v += "\\begin{verbatim}\n"; + v.append (l, n); + v += "\n\\end{verbatim}"; + } + else + { + format_line (ot, v, l, n); + } + + break; + } case ot_man: { if (b.para) @@ -2019,6 +2286,7 @@ format (semantics::scope& scope, string const& s, bool para) break; } + case ot_latex: break; case ot_man: break; } } @@ -2213,6 +2481,59 @@ format (semantics::scope& scope, string const& s, bool para) break; } + case ot_latex: + { + if (!v.empty ()) + v += "\n\n"; + + switch (pb.kind) + { + case block::h: + { + char t (ph[0]); + + string label; + string s (latex_section( options.latex_section_map (), t)); + + if (!pi.empty ()) + label = "\\label{" + pi + '}'; + + v += '\\' + s + '{' + label + pv + '}'; + + break; + } + case block::ul: v += "\\begin{itemize}\n" + pv + "\n\\end{itemize}"; break; + case block::ol: v += "\\begin{enumerate}\n" + pv + "\n\\end{enumerate}"; break; + case block::dl: v += "\\begin{description}\n" + pv + "\n\\end{description}"; break; + case block::li: + { + latex_margin (pv); // Strip leading/trailing newline + + if (b.kind == block::dl) + { + latex_margin (ph); // Strip leading/trailing newline + + v += "\\item[{" + ph + "}] " + pv; + } + else + v += "\\item " + pv; + + break; + } + case block::note: + { + v += "\\begin{tcolorbox}\n"; + v += pv; + v += "\n\\end{tcolorbox}\n"; + + break; + } + case block::text: + case block::pre: assert (false); + } + + break; + } case ot_man: { // Seeing that we always write a macro, one newline is enough. @@ -2305,6 +2626,10 @@ start_toc () tocs.push_back (toc_entry ('\0')); return " <table class=\"toc\">"; } + case ot_latex: + { + return "\\tableofcontents{}\n"; + } case ot_man: break; } @@ -2346,6 +2671,7 @@ end_toc () v += " </table>"; return v; } + case ot_latex: break; case ot_man: break; } |