module doc_reform.io_out.latex; template outputLaTeX() { import doc_reform.io_out; import std.digest.sha, std.file, std.outbuffer, std.uri, std.conv : to; mixin InternalMarkup; // watch mixin spineOutputRgxInit; static auto rgx = Rgx(); mixin spineLanguageCodes; auto lang = Lang(); auto paper() { struct PaperType { @safe auto a4() { struct A4 { auto portrait() { struct V { uint w = 160; uint h = 228; uint img_px = 450; } return V(); } auto landscape() { struct H { uint w = 238; uint h = 160; uint img_px = 300; } return H(); } } return A4(); } @safe auto a5() { struct A5 { auto portrait() { struct V { uint w = 112; uint h = 162; uint img_px = 280; } return V(); } auto landscape() { struct H { uint w = 152; uint h = 100; uint img_px = 190; } return H(); } } return A5(); } @safe auto b4() { struct B4 { auto portrait() { struct V { uint w = 140; uint h = 204; uint img_px = 356; } return V(); } auto landscape() { struct H { uint w = 200; uint h = 130; uint img_px = 260; } return H(); } } return B4(); } @safe auto letter() { struct Letter { auto portrait() { struct V { uint w = 166; uint h = 212; uint img_px = 468; } return V(); } auto landscape() { struct H { uint w = 226; uint h = 166; uint img_px = 290; } return H(); } } return Letter(); } @safe auto legal() { struct Legal { auto portrait() { struct V { uint w = 168; uint h = 286; uint img_px = 474; } return V(); } auto landscape() { struct H { uint w = 296; uint h = 166; uint img_px = 420; } return H(); } } return Legal(); } } return PaperType(); } @safe string sp_char_esc(O)( string _txt, const O obj, ) { string _unescape_sp_char_esc()(string _txt) { _txt = _txt .replaceAll(rgx.latex_special_char_escaped, format(q"┃%s┃", "$1")) .replaceAll(rgx.latex_special_char_escaped_braced, format(q"┃%s┃", "$1")); return _txt; } string _unescape_fontface_esc()(string _txt) { _txt = _txt.replaceAll(rgx.latex_identify_inline_fontface, format(q"┃%s%s┃", "$1", "$2")); return _txt; } if (obj.metainfo.is_a != "code") { _txt = replaceAll!(m => "\\" ~ m[1])(_txt, rgx.latex_special_char_for_escape); _txt = replaceAll!(m => "{\\" ~ m[1] ~ "}")(_txt, rgx.latex_special_char_for_escape_and_braces); _txt = replaceAll!(m => _unescape_sp_char_esc(m[0]))(_txt, rgx.latex_identify_inline_link); _txt = replaceAll!(m => _unescape_fontface_esc(m[0]))(_txt, rgx.latex_identify_inline_fontface); } return _txt; } @safe string sp_char_esc_txt()( string _txt, ) { string _unescape_sp_char_esc()(string _txt) { _txt = _txt .replaceAll(rgx.latex_special_char_escaped, format(q"┃%s┃", "$1")) .replaceAll(rgx.latex_special_char_escaped_braced, format(q"┃%s┃", "$1")); return _txt; } string _unescape_fontface_esc()(string _txt) { _txt = _txt.replaceAll(rgx.latex_identify_inline_fontface, format(q"┃%s%s┃", "$1", "$2")); return _txt; } _txt = replaceAll!(m => "\\" ~ m[1])(_txt, rgx.latex_special_char_for_escape); _txt = replaceAll!(m => "{\\" ~ m[1] ~ "}")(_txt, rgx.latex_special_char_for_escape_and_braces); _txt = replaceAll!(m => _unescape_sp_char_esc(m[0]))(_txt, rgx.latex_identify_inline_link); _txt = replaceAll!(m => _unescape_fontface_esc(m[0]))(_txt, rgx.latex_identify_inline_fontface); return _txt; } @safe string fontface()( string _txt, ) { _txt = _txt .replaceAll(rgx.inline_emphasis, format(q"┃\begin{bfseries}%s\end{bfseries}┃", "$1")) .replaceAll(rgx.inline_bold, format(q"┃\begin{bfseries}%s\end{bfseries}┃", "$1")) .replaceAll(rgx.inline_italics, format(q"┃\emph{%s}┃", "$1")) .replaceAll(rgx.inline_italics, format(q"┃\uline{%s}┃", "$1")) .replaceAll(rgx.inline_superscript, format(q"┃$$^{\textrm{%s}}$$┃", "$1")) .replaceAll(rgx.inline_subscript, format(q"┃$$_{\textrm{%s}}$$┃", "$1")) .replaceAll(rgx.inline_strike, format(q"┃\sout{%s}┃", "$1")) .replaceAll(rgx.inline_insert, format(q"┃\uline{%s}┃", "$1")) .replaceAll(rgx.inline_mono, format(q"┃\begin{monosp}%s\end{monosp}┃", "$1")) .replaceAll(rgx.inline_italics, format(q"┃``%s''┃", "$1")); return _txt; } @safe string leading_hardspaces()( string _txt, ) { string hardspaces(string _spaces) { _spaces = _spaces .replaceAll(rgx.space, "\\hardspace "); return _spaces; } _txt = replaceAll!(m => hardspaces(m[0]))(_txt, rgx.spaces_line_start); return _txt; } @safe string nbsp_char()(string _txt) { if (_txt.match(rgx.nbsp_char)) { _txt = _txt.replaceAll(rgx.nbsp_char, "\\hardspace "); } return _txt; } @safe string nbsp_char_to_space()(string _txt) { if (_txt.match(rgx.nbsp_char)) { _txt = _txt.replaceAll(rgx.nbsp_char, " "); } return _txt; } @safe string links_and_images(O,M)( string _txt, const O obj, M doc_matters, ) { if (obj.has.inline_links) { // TODO some images do not have inline links ... image without link string _width_adjust(string _width) { if (_width.to!int > 300) { _width = "300"; } // will need to vary max with papersize & orientation return _width; } string _latex_image_path(string _image_path) { auto pth_latex = spinePathsLaTeX(doc_matters); _image_path = pth_latex.latex_path_stuff ~ "/" ~ _image_path; return _image_path; } string _if_images(string _linked_content) { if (_linked_content.match(rgx.inline_image_info)) { _linked_content = replaceAll!(m => format(q"┃\includegraphics*[width=%spt]{%s}%s┃", _width_adjust(m[2]), _latex_image_path(m[1]), " \\\\\n") )(_linked_content, rgx.inline_image_info); } return _linked_content; } string _check_link(string _link) { _link = _link .replaceFirst(rgx.latex_clean_internal_link, "") .replaceAll(rgx.latex_special_char_for_escape_url, "\\$1"); return _link; } if (obj.metainfo.is_a != "code") { _txt = replaceAll!(m => m[1] ~ "┤" ~ to!string((obj.stow.link[m[2].to!ulong])).encode ~ "├" )(_txt, rgx.inline_link_number_only); _txt = replaceAll!(m => ((m[1] == m[2]) && (m[2].match(rgx.uri))) ? format(q"┃\begin{scriptsize}\lefthalfcap\url{%s}\righthalfcup\end{scriptsize}┃", _check_link(m[1])) : (m[2].match(rgx.uri)) // ERROR ? format(q"┃%s\href{%s}%s{%s}┃", "\\\\\n", _check_link(m[2]), "\n", _if_images(m[1])) : format(q"┃\hyperlink{%s}{%s}┃", _check_link(m[2]), _if_images(m[1])) )(_txt, rgx.inline_link); } } return _txt; } @safe string footnotes()( string _txt, ) { if (_txt.match(rgx.inline_notes_al_gen)) { string _tex_note = q"┃\hypertarget{noteref_%s}{}\footnote[%s]{%% \label{note_%s}%s}┃"; _txt = _txt.replaceAll(rgx.inline_notes_al_regular_number_note, format(_tex_note, "$1", "$1", "$1", "$2".strip ).strip ); } return _txt; } @safe string remove_footnotes()( string _txt, ) { if (_txt.match(rgx.inline_notes_al_gen)) { _txt = replaceAll!(m => "")(_txt, rgx.inline_notes_al_gen); } return _txt; } @safe string para(O)( string _txt, O obj, ) { if (obj.metainfo.is_of_type == "para") { string _tex_para; _tex_para = q"┃\begin{tiny}\hspace{0mm}\end{tiny}{\marginpar{\begin{tiny}\hspace{0mm}\hypertarget{%s}{%s}\end{tiny}}}%s┃"; _txt = format(_tex_para, obj.metainfo.object_number, obj.metainfo.object_number, _txt.footnotes ).strip; } return _txt; } @safe string bookindex(O)( string _txt, O obj, ) { if (obj.metainfo.is_of_type == "para" && obj.metainfo.is_a == "bookindex" ) { string _tex_para; _tex_para = q"┃%s┃"; _txt = format(_tex_para, _txt.replaceAll(rgx.latex_clean_bookindex_linebreak, "\n") ~ "\n\\\\\n" ); } return _txt; } @safe string heading(O,M)( string _txt, O obj, M doc_matters, string _part = "" ) { if (obj.metainfo.is_a == "heading") { string _tex_para; string _pg_break; string _sect; string _post; string _title_add; string _columns = ""; switch (obj.metainfo.heading_lev_markup) { case 0: // A == TITLE _pg_break = "\\begin{document}\n"; goto default; case 1: // B == part: section heading level _pg_break = "\\clearpage\n"; goto default; case 2: // C == part: section heading level _pg_break = "\\clearpage\n"; goto default; case 3: // D == part: section heading level _pg_break = "\\clearpage\n"; goto default; case 4: // 1 == section _columns = (_part != "bookindex") ? "" : "\n\\\\\n\\begin{multicols}{2}"; _pg_break = "\\clearpage\n"; _sect = "section"; _post = ""; _title_add = format(q"┃ \markboth{%s}{%s}┃", doc_matters.conf_make_meta.meta.title_full, doc_matters.conf_make_meta.meta.title_full, ); goto default; case 5: // 2 == subsection _pg_break = ""; // _pg_break = "newpage"; // doubt this is necessary _sect = "subsection"; _post = " \\\n"; _title_add = ""; goto default; case 6: // 3 == subsubsection _pg_break = ""; // _pg_break = "newpage"; // doubt this is necessary _sect = "subsubsection"; _post = " \\\n"; _title_add = ""; goto default; default: if (obj.metainfo.heading_lev_markup == 0) { _tex_para = q"┃\begin{document} \title{%s} \author{ \textnormal{%s}} \date{\begin{tiny}%s\end{tiny}} \pagenumbering{roman}\maketitle \pagestyle{fancy} \newpage \markboth{%s}{%s} \\\\[3]\ \linebreak Copyright {\begin{small}{\copyright\end{small}} %s \\ %s \pagestyle{fancy} \clearpage┃"; _txt = format(_tex_para, (doc_matters.conf_make_meta.meta.title_full).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.creator_author).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.date_published).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.title_main).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.title_full).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.rights_copyright).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.rights_license).sp_char_esc_txt, ); } else if (obj.metainfo.heading_lev_markup < 4) { if (!(_txt.footnotes.strip == "Endnotes")) { _tex_para = q"┃%s\part*{\begin{tiny}\hspace{0mm}\end{tiny}{\marginpar{\begin{tiny}\hspace{0mm}\hypertarget{%s}{%s}\end{tiny}}}%s} \addcontentsline{toc}{part}{%s} \markboth{%s}┃"; _txt = format(_tex_para, _pg_break, obj.metainfo.object_number, obj.metainfo.object_number, _txt.strip.footnotes, _txt.strip.remove_footnotes, (doc_matters.conf_make_meta.meta.title_main).sp_char_esc_txt, ); } } else if (obj.metainfo.heading_lev_markup > 3) { if (obj.metainfo.heading_lev_markup == 4 && _txt.match(regex(r"^Table of Contents$"))) { _tex_para = q"┃ \pagenumbering{none} \setcounter{page}{1} \setlength{\parskip}{1ex plus0.5ex minus0.2ex} \part*{\begin{tiny}\hspace{0mm}\end{tiny}{\marginpar{\begin{tiny}\hspace{0mm}\hypertarget{1}{1}\end{tiny}}}%s \newline %s} \clearpage \markboth{%s}{%s} \pagenumbering{gobble} \renewcommand{\contentsname}{} \tableofcontents \markboth{%s}{%s} \clearpage \pagenumbering{arabic} \setcounter{page}{1} \markboth{%s}{%s} %% \null \clearpage \setcounter{page}{1}┃"; _txt = format(_tex_para, (doc_matters.conf_make_meta.meta.title_full).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.creator_author).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.title_main).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.title_main).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.title_full).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.title_full).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.title_full).sp_char_esc_txt, (doc_matters.conf_make_meta.meta.title_full).sp_char_esc_txt, ); } else if (obj.metainfo.heading_lev_markup == 4 && _part == "bookindex" && _txt.match(regex(r"^Index$")) ) { _tex_para = q"┃%s\%s*{\begin{tiny}\hspace{0mm}\end{tiny}{\marginpar{\begin{tiny}\hspace{0mm}\hypertarget{%s}{%s}\end{tiny}}}%s} \addcontentsline{toc}{%s}{%s%s}%s%s┃"; _txt = format(_tex_para, _pg_break, _sect.strip, obj.metainfo.object_number, obj.metainfo.object_number, _txt.footnotes.strip, _sect, _txt.remove_footnotes.strip, _post, _title_add, _columns, ); } else if (obj.metainfo.dummy_heading && obj.metainfo.heading_lev_markup == 4 ) { /+ dummy headings completely omitted +/ _txt = ""; } else { _tex_para = q"┃%s\%s*{\begin{tiny}\hspace{0mm}\end{tiny}{\marginpar{\begin{tiny}\hspace{0mm}\hypertarget{%s}{%s}\end{tiny}}}%s} \addcontentsline{toc}{%s}{%s%s}%s┃"; _txt = format(_tex_para, _pg_break, _sect.strip, obj.metainfo.object_number, obj.metainfo.object_number, _txt.footnotes.strip, _sect, _txt.remove_footnotes.strip, _post, _title_add, ); } } break; } } return _txt.strip; } string group(O,M)( string _txt, O obj, M doc_matters, ) { if (obj.metainfo.is_a == "group") { string _tex_para; _tex_para = q"┃\begin{tiny}\hspace{0mm}\end{tiny}{\marginpar{\begin{tiny}\hspace{0mm}\hypertarget{%s}{%s}\end{tiny}}}\setlength{\parskip}{0.5ex plus0.2ex minus0.1ex} \begin{footnotesize} %s \end{footnotesize} ┃"; _txt = format(_tex_para, obj.metainfo.object_number, obj.metainfo.object_number, _txt.footnotes.strip ).strip; } return _txt; } string block(O,M)( string _txt, O obj, M doc_matters, ) { if (obj.metainfo.is_a == "block") { // _txt = _txt.nbsp_char; string _tex_para; _tex_para = q"┃\begin{tiny}\hspace{0mm}\end{tiny}{\marginpar{\begin{tiny}\hspace{0mm}\hypertarget{%s}{%s}\end{tiny}}}\setlength{\parskip}{0.5ex plus0.2ex minus0.1ex} \begin{footnotesize} %s \end{footnotesize} \setlength{\parskip}{1ex plus0.5ex minus0.2ex} ┃"; // \hardspace /+ try both: +/ _txt = _txt.split(rgx.br_newlines_linebreaks).join("\n\n"); // _txt = _txt.split(rgx.br_newlines_linebreaks).join(" \\\n"); _txt = format(_tex_para, obj.metainfo.object_number, obj.metainfo.object_number, _txt.nbsp_char.footnotes.strip ).strip; } return _txt; } string verse(O,M)( string _txt, O obj, M doc_matters, ) { if (obj.metainfo.is_a == "verse") { string _tex_para; _tex_para = q"┃\begin{tiny}\hspace{0mm}\end{tiny}{\marginpar{\begin{tiny}\hspace{0mm}\hypertarget{%s}{%s}\end{tiny}}}\setlength{\parskip}{0.1ex plus0.1ex minus0.1ex} \begin{footnotesize} %s \end{footnotesize} \setlength{\parskip}{1ex plus0.5ex minus0.2ex} \linebreak ┃"; // \hardspace _txt = format(_tex_para, obj.metainfo.object_number, obj.metainfo.object_number, _txt.nbsp_char.footnotes.split("\n").join("\n\n").strip ).strip; } return _txt; } string codeblock(O,M)( string _txt, O obj, M doc_matters, ) { if (obj.metainfo.is_a == "code") { string _tex_para; _tex_para = q"┃\begin{tiny}\hspace{0mm}\end{tiny}{\marginpar{\begin{tiny}\hspace{0mm}\hypertarget{%s}{%s}\end{tiny}}}\setlength{\parskip}{0.5ex plus0.2ex minus0.1ex}\begin{Codeblock} \begin{lstlisting} %s \end{lstlisting} \end{Codeblock} \setlength{\parskip}{1ex plus0.5ex minus0.2ex} ┃"; _txt = format(_tex_para, obj.metainfo.object_number, obj.metainfo.object_number, _txt.nbsp_char_to_space ).strip; } return _txt; } auto tablarize(O)( string _txt, const O obj, ) { string[] _table_rows = (_txt).split(rgx.table_delimiter_row); string[] _table_cols; string _table; string _tablenote; foreach(row_idx, row; _table_rows) { _table_cols = row.split(rgx.table_delimiter_col); _table ~= ""; foreach(col_idx, cell; _table_cols) { if ((_table_cols.length == 1) && (_table_rows.length <= row_idx+2)) { // check row_idx+2 (rather than == ++row_idx) _tablenote ~= cell; } else { // // _table ~= "\\bfseries "; // _table ~= cell; // _table ~= (_table_cols.length > (col_idx + 1)) ? "&" : ""; _table ~= format(q"┃%s%s┃", cell, (_table_cols.length > (col_idx + 1)) ? "&" : "" ); } } _table ~= "\\\\"; } Tuple!(string, string) t = tuple( _table, _tablenote, ); return t; } string table(O,M)( string _txt, O obj, M doc_matters, ) { if (obj.metainfo.is_a == "table") { auto _t = _txt.tablarize(obj); string _table = _t[0]; string _t_n = _t[1]; string papertype = "a4"; uint pw = 0; switch (papertype) { case "a4": pw = (paper.a4.portrait.w - 20); break; case "a5": pw = (paper.a5.portrait.w - 20); break; case "b4": pw = (paper.b4.portrait.w - 20); break; case "letter": pw = (paper.letter.portrait.w - 20); break; case "legal": pw = (paper.legal.portrait.w - 20); break; default: pw = 0; break; } // auto textwidth = (pw - 24); string _colw = ""; foreach (w; obj.table.column_widths) { _colw ~= format(q"┃p{%.0fmm}┃", (w * pw / 100) // (w * (pw - 24)/ 100) // (w * textwidth / 100) ); } string _tex_para; _tex_para = q"┃\begin{tiny}\hspace{0mm}\end{tiny}{\marginpar{\begin{tiny}\hspace{0mm}\hypertarget{%s}{%s}\end{tiny}}} \setlength{\LTleft}{0pt} \setlength{\LTright}{\fill} \begin{tiny} \begin{longtable}{%s} %s \end{longtable} \end{tiny} ┃"; _txt = format(_tex_para, obj.metainfo.object_number, obj.metainfo.object_number, _colw, _table, ).strip; } return _txt; } @safe string bullets_and_indentation(O)( string _txt, O obj, ) { string _tex_para; string _hang; string _indent; int _paper_margin = -10; int _indent_increment = 8; // 5; 10; if (obj.attrib.bullet) { int _bullet_space = 5; _indent = ((obj.attrib.indent_base * _indent_increment) + _paper_margin + _bullet_space).to!string; _txt = format(q"┃\begin{Bullet}{%smm}$\txtbullet$\hspace{\enspace}%s\end{Bullet}┃", _indent, _txt.footnotes ).strip; } else if ( obj.attrib.indent_base != 0 && obj.attrib.indent_base == obj.attrib.indent_hang ) { _indent = ((obj.attrib.indent_base * _indent_increment) + _paper_margin).to!string; _tex_para = q"┃\begin{ParagraphIndent}{%smm}%s \end{ParagraphIndent}┃"; _txt = format(_tex_para, _indent, _txt.footnotes ).strip; } else if ( obj.attrib.indent_base != 0 || obj.attrib.indent_hang != 0 ) { _indent = ((obj.attrib.indent_base * _indent_increment) + _paper_margin).to!string; _hang = (((obj.attrib.indent_hang - obj.attrib.indent_base) * _indent_increment)).to!string; _tex_para = q"┃\begin{ParagraphHang}{%smm}{%smm}%s \end{ParagraphHang}┃"; _txt = format(_tex_para, _indent, _hang, _txt.footnotes ).strip; } return _txt; } string latex_head(M)( M doc_matters, ) { struct paperType { string a4_portrait; string a4_landscape; string us_letter_portrait; string us_letter_landscape; } auto paper = paperType(); string _footer(M)(M doc_matters) { string _ft = "\\lfoot[\\textrm{\\thepage}]"; string _ft_1 = format(q"┃{\tiny \href{%s}{%s}}┃", "http://sisudoc.org", "SiSU",); string _ft_2 = format(q"┃ \cfoot{\href{%s}{%s}}┃", "http://git.sisudoc.org", "git",); if (doc_matters.conf_make_meta.make.footer.length > 0) { if (doc_matters.conf_make_meta.make.footer.length > 0) { if (doc_matters.conf_make_meta.make.footer[0].matchAll(rgx.inline_link)) { _ft ~= doc_matters.conf_make_meta.make.footer[0] .replace(rgx.inline_link, "{\\tiny \\href{$2}{$1}}"); } else { _ft ~= _ft_1; } } if (doc_matters.conf_make_meta.make.footer.length > 1) { if (doc_matters.conf_make_meta.make.footer[1].matchAll(rgx.inline_link)) { _ft ~= doc_matters.conf_make_meta.make.footer[1] .replace(rgx.inline_link, "\n\\cfoot{\\href{$2}{$1}}"); } else { _ft ~= _ft_2; } } } else { _ft ~= _ft_1; _ft ~= _ft_2; } return _ft; } paper.a4_portrait = format(q"┃ \documentclass[12pt,a4paper,titlepage]{scrartcl} \setlength{\textheight}{228mm} \setlength{\textwidth}{160mm} ┃", ); paper.a4_landscape = format(q"┃ \documentclass[11pt,a4paper,landscape,titlepage,twocolumn]{scrartcl} \setlength{\textheight}{160mm} \setlength{\textwidth}{238mm} ┃", ); paper.us_letter_portrait = format(q"┃ \documentclass[12pt,letterpaper,titlepage]{scrartcl} \setlength{\textheight}{212mm} \setlength{\textwidth}{166mm} ┃", ); paper.us_letter_landscape = format(q"┃ \documentclass[11pt,letterpaper,landscape,titlepage,twocolumn]{scrartcl} \setlength{\textheight}{166mm} \setlength{\textwidth}{226mm} ┃", ); struct paperMargins { string portrait; string landscape; } auto margins = paperMargins(); margins.portrait = format(q"┃ \setlength{\oddsidemargin}{0mm} \setlength{\evensidemargin}{0mm} \setlength{\topmargin}{-12pt} \setlength{\headheight}{12pt} \setlength{\headsep}{35pt} ┃", ); margins.landscape = format(q"┃ \setlength{\oddsidemargin}{6mm} \setlength{\evensidemargin}{6mm} \setlength{\topmargin}{-12mm} \setlength{\headheight}{12pt} \setlength{\headsep}{20pt} ┃", ); struct columnsMulti { string portrait; string landscape; } auto multicol = columnsMulti(); multicol.portrait = format(q"┃ \usepackage{multicol} ┃", ); multicol.landscape = ""; struct colorLinks { string mono; string color; } auto links = colorLinks(); links.mono = format(q"┃ \usepackage[xetex, colorlinks=true, urlcolor=myblack, filecolor=myblack, linkcolor=myblack, ┃", ); links.color = format(q"┃ \usepackage[xetex, colorlinks=true, urlcolor=myblue, %% \href{...}{...} external url filecolor=mygreen, %% \href{...} local file linkcolor=myred, %% \href{...} and \pageref{...} ┃", ); string _latex_head = format(q"┃%%%% spine LaTeX output %%%% Generated by: %s %%%% D version: %s %%%% LaTeX output last Generated on: %s %%%% %s %s %s %s \setlength{\marginparsep}{4mm} \setlength{\marginparwidth}{8mm} %s \usepackage{polyglossia, ucs, fontspec, xltxtra, xunicode} \setmainlanguage{%s} \setotherlanguage{%s} \setmainfont{Liberation Sans} \setmonofont[Scale=0.85]{Liberation Mono} \usepackage{alltt} \usepackage{thumbpdf} %s pdftitle={%s}, pdfauthor={%s}, pdfsubject={%s}, pdfkeywords={}, pageanchor=true, plainpages=true, pdfpagelabels=true, pagebackref, bookmarks=true, bookmarksopen=true, pdfmenubar=true, pdfpagemode=UseOutline, pdffitwindow=true, pdfwindowui=true, plainpages=false, pdfstartview=FitH ] {hyperref} \usepackage[usenames]{color} \definecolor{myblack}{rgb}{0,0,0} \definecolor{myred}{rgb}{0.75,0,0} \definecolor{mygreen}{rgb}{0,0.5,0} \definecolor{myblue}{rgb}{0,0,0.5} \definecolor{mywhite}{rgb}{1,1,1} \usepackage{url} \urlstyle{sf} \usepackage{textcomp} \usepackage[parfill]{parskip} \usepackage[normalem]{ulem} \usepackage{soul} \usepackage{longtable} \usepackage[tc]{titlepic} \usepackage{graphicx} \makeatletter \parindent0pt \usepackage{amssymb} \usepackage{listings} \usepackage{color} \usepackage{textcomp} \setcounter{secnumdepth}{2} \setcounter{tocdepth}{4} \makeatletter \usepackage[multiple,ragged]{footmisc} \setlength\footnotemargin{12pt} \usepackage[para]{manyfoot} \DeclareNewFootnote{A} \newenvironment{ParagraphIndent}[1]%% { \begin{list}{}{%% \setlength\topsep{0pt}%% \addtolength{\leftmargin}{#1} \setlength\parsep{0pt plus 1pt}%% } \item[] } {\end{list}} \newenvironment{ParagraphHang}[2]%% { \begin{list}{}{%% \setlength\topsep{0pt}%% \addtolength{\leftmargin}{#1} \itemindent=#2 \setlength\parsep{0pt plus 1pt}%% } \item[] } {\end{list}} \newenvironment{Bullet}[1]%% { \begin{list}{}{%% \setlength\topsep{0pt}%% \addtolength{\leftmargin}{#1} \itemindent=-1em \setlength\parsep{0pt plus 1pt}%% } \item[] } {\end{list}} \usepackage{fancyhdr} \lhead{} \renewcommand{\part}{\@startsection {part}{1}{-2mm}%% {-\baselineskip}{0.5\baselineskip}%% {\bfseries\large\upshape\raggedright}} \renewcommand{\section}{\@startsection {section}{2}{-2mm}%% {-\baselineskip}{0.5\baselineskip}%% {\bfseries\large\upshape\raggedright}} \renewcommand{\subsection}{\@startsection {subsection}{3}{-2mm}%% {-\baselineskip}{0.5\baselineskip}%% {\bfseries\large\upshape\raggedright}} \renewcommand{\subsubsection}{\@startsection {subsubsection}{4}{-2mm}%% {-\baselineskip}{0.5\baselineskip}%% {\normalfont\normalsize\bfseries\raggedright}} \renewcommand{\paragraph}{\@startsection {paragraph}{5}{-2mm}%% {-\baselineskip}{0.5\baselineskip}%% {\normalfont\normalsize\itshape\raggedright}} \renewcommand{\subparagraph}{\@startsection {subparagraph}%%{6}%%{-2mm}%% {-\baselineskip}{0.5\baselineskip}%% {\normalfont\normalsize\itshape\raggedright}} \selectlanguage{%s} \lhead[ ]{ } \chead[ \fancyplain{} \bfseries \footnotesize \leftmark ]{ \fancyplain{} \bfseries \footnotesize \rightmark } \rhead[ ]{ } %s \rfoot[\tiny \href{}{}]{\textrm{\thepage}} \tolerance=300 \clubpenalty=300 \widowpenalty=300 \makeatother \makeatother \chardef\txtbullet="2022 \chardef\tilde="7E \def\asterisk{{\rm \char42} } \definecolor{Light}{gray}{.92} \newcommand{\Codeblock}[1]{\normaltext\raggedright\small\ttfamily\texbackslash#1} \newcommand{\monosp}[1]{\normaltext\ttfamily\texbackslash#1} \newcommand{\parasep}{\\ \begin{center}*\hspace{2em}*\hspace{2em}*\end{center} \\} \newcommand{\hardspace}{{~}} \newcommand{\caret}{{\^{~}}} \newcommand{\pipe}{{\textbar}} \newcommand{\curlyopen}{{} \newcommand{\curlyclose}{}} \newcommand{\lt}{{UseTextSymbol{OML}{<}}} \newcommand{\gt}{{UseTextSymbol{OML}{>}}} \newcommand{\slash}{{/}} \newcommand{\underscore}{\_} \newcommand{\exclaim}{\Verbatim{!}} \definecolor{listinggray}{gray}{0.9} \definecolor{lbcolor}{rgb}{0.9,0.9,0.9} \lstset{ backgroundcolor=\color{lbcolor}, tabsize=4, rulecolor=, language=, basicstyle=\scriptsize, upquote=true, aboveskip={1.5\baselineskip}, columns=fixed, showstringspaces=false, extendedchars=true, breaklines=true, prebreak = \raisebox{0ex}[0ex][0ex]{\ensuremath{\hookleftarrow}}, frame=single, showtabs=false, showspaces=false, showstringspaces=false, identifierstyle=\ttfamily, keywordstyle=\color[rgb]{0,0,1}, commentstyle=\color[rgb]{0.133,0.545,0.133}, stringstyle=\color[rgb]{0.627,0.126,0.941}, } ┃", doc_matters.opt.action.debug_do ? "" : doc_matters.generator_program.name_and_version.strip, doc_matters.opt.action.debug_do ? "" : doc_matters.generator_program.compiler.strip, doc_matters.opt.action.debug_do ? "" : doc_matters.generator_program.stime.strip, doc_matters.generator_program.project_name.strip, doc_matters.generator_program.url_home.strip, paper.a4_portrait.strip, margins.portrait.strip, multicol.portrait.strip, lang.codes[doc_matters.src.language]["xlp"], "english", links.mono.strip, // links.color.strip, doc_matters.conf_make_meta.meta.title_full.strip, doc_matters.conf_make_meta.meta.creator_author.strip, doc_matters.conf_make_meta.meta.classify_subject.strip, lang.codes[doc_matters.src.language]["xlp"], _footer(doc_matters), ); return _latex_head.strip; } string latex_body(D,M)( const D doc_abstraction, M doc_matters, ) { string _latex_body = ""; bool _multicolumns = false; string _txt; foreach (part; doc_matters.has.keys_seq.latex) { foreach (obj; doc_abstraction[part]) { switch (obj.metainfo.is_of_part) { case "frontmatter": assert(part == "head" || "toc"); _txt = obj.text .sp_char_esc(obj) .fontface; switch (obj.metainfo.is_of_type) { case "para": switch (obj.metainfo.is_a) { case "heading": _txt = _txt.heading(obj, doc_matters); goto default; case "toc": break; default: _latex_body ~= _txt ~ "\n\n"; _txt = ""; break; } break; default: break; } break; case "body": assert(part == "body" || "head"); // surprise _txt = obj.text .sp_char_esc(obj) .fontface; switch (obj.metainfo.is_of_type) { case "para": switch (obj.metainfo.is_a) { case "heading": _txt = _txt.heading(obj, doc_matters); goto default; case "para": _txt = _txt.para(obj) .bullets_and_indentation(obj) .links_and_images(obj, doc_matters); goto default; default: _latex_body ~= _txt ~ "\n\n"; _txt = ""; break; } break; case "block": switch (obj.metainfo.is_a) { case "quote": goto default; // TODO case "group": /+ (hardspaces not honored) [remove any hardspace marker] +/ _txt = _txt.group(obj, doc_matters) .links_and_images(obj, doc_matters); goto default; case "block": /+ (hardspace honored) \hardspace +/ _txt = _txt.block(obj, doc_matters) .links_and_images(obj, doc_matters); goto default; case "verse": /+ (hardspace honored) \hardspace +/ _txt = _txt.verse(obj, doc_matters) .links_and_images(obj, doc_matters); goto default; case "code": /+ (hardspace honored) \begin{lstlisting} clear hardspace marker +/ _txt = _txt.codeblock(obj, doc_matters); goto default; case "table": _txt = _txt.table(obj, doc_matters); goto default; // TODO default: _latex_body ~= _txt ~ "\n\n"; _txt = ""; break; } break; default: break; } break; case "backmatter": assert(part == "endnotes" || "glossary" || "bibliography" || "bookindex" || "blurb" || "tail"); _txt = obj.text .sp_char_esc(obj) .fontface; switch (obj.metainfo.is_of_type) { case "para": if (part != "bookindex" && _multicolumns) { _multicolumns = false; _latex_body ~= "\n\\end{multicols}\n"; } switch (obj.metainfo.is_a) { case "heading": if (part == "bookindex") { _multicolumns = true; } _txt = _txt.heading(obj, doc_matters, part); goto default; case "endnote": assert(part == "endnotes"); /* uncomment code to reinstate endnotes in endnote section */ // _txt = _txt.para(obj) // .bullets_and_indentation(obj) // .links_and_images(obj, doc_matters); // goto default; break; case "glossary": assert(part == "glossary"); _txt = _txt.para(obj) .bullets_and_indentation(obj) .links_and_images(obj, doc_matters); goto default; case "bibliography": assert(part == "bibliography"); _txt = _txt.para(obj) .bullets_and_indentation(obj); goto default; case "bookindex": assert(part == "bookindex"); /+ two column, special section +/ _txt = _txt.bookindex(obj) .links_and_images(obj, doc_matters); goto default; case "blurb": assert(part == "blurb"); _txt = _txt.para(obj) .bullets_and_indentation(obj) .links_and_images(obj, doc_matters); goto default; default: _latex_body ~= (part == "bookindex" && obj.metainfo.is_a != "heading") ? _txt : (_txt ~ "\n\n"); _txt = ""; break; } break; default: break; } break; case "comment": break; default: { /+ debug +/ if (doc_matters.opt.action.debug_do && doc_matters.opt.action.verbose) { writeln(__FILE__, ":", __LINE__, ": ", obj.metainfo.is_of_part); writeln(__FILE__, ":", __LINE__, ": ", obj.metainfo.is_a); writeln(__FILE__, ":", __LINE__, ": ", obj.text); } } break; } } } if (_multicolumns) { _multicolumns = false; _latex_body ~= "\n\\end{multicols}\n"; } return _latex_body; } string latex_tail(M)( M doc_matters, ) { string _latex_tail = format(q"┃ \end{document} ┃", // doc_matters.conf_make_meta.meta.title_full, // doc_matters.conf_make_meta.meta.creator_author, ); return _latex_tail; } void writeOutputLaTeX(T,M)( const T latex_content, M doc_matters, ) { auto pth_latex = spinePathsLaTeX(doc_matters); try { { /+ debug +/ if (doc_matters.opt.action.debug_do && doc_matters.opt.action.verbose) { writeln(latex_content.head); writeln(latex_content.content); writeln(latex_content.tail); } } if (!exists(pth_latex.latex_path_stuff)) { (pth_latex.latex_path_stuff).mkdirRecurse; } if (!(doc_matters.opt.action.quiet)) { writeln(" ", pth_latex.latex_file_with_path); } auto f = File(pth_latex.latex_file_with_path, "w"); f.writeln(latex_content.head); f.writeln(latex_content.content); f.writeln(latex_content.tail); foreach (image; doc_matters.srcs.image_list) { string fn_src_in = doc_matters.src.image_dir_path ~ "/" ~ image; string fn_src_out_file = pth_latex.latex_path_stuff ~ "/" ~ image; if (exists(fn_src_in)) { fn_src_in.copy(fn_src_out_file); } } } catch (ErrnoException ex) { // handle error } } void outputLaTeX(D,M)( const D doc_abstraction, M doc_matters, ) { struct LaTeX { string head; string content; string tail; } auto latex = LaTeX(); latex.head = latex_head(doc_matters); latex.content = latex_body(doc_abstraction, doc_matters); latex.tail = latex_tail(doc_matters); latex.writeOutputLaTeX(doc_matters); } }