-*- mode: org -*-
#+TITLE:       spine (doc_reform) output latex
#+DESCRIPTION: documents - structuring, publishing in multiple formats & search
#+FILETAGS:    :spine:output:latex:pdf:
#+AUTHOR:      Ralph Amissah
#+EMAIL:       [[mailto:ralph.amissah@gmail.com][ralph.amissah@gmail.com]]
#+COPYRIGHT:   Copyright (C) 2015 - 2024 Ralph Amissah
#+LANGUAGE:    en
#+STARTUP:     content hideblocks hidestars noindent entitiespretty
#+PROPERTY:    header-args  :exports code
#+PROPERTY:    header-args+ :noweb yes
#+PROPERTY:    header-args+ :results no
#+PROPERTY:    header-args+ :cache no
#+PROPERTY:    header-args+ :padline no
#+PROPERTY:    header-args+ :mkdirp yes
#+OPTIONS:     H:3 num:nil toc:t \n:t ::t |:t ^:nil -:t f:t *:t

- [[./doc-reform.org][doc-reform.org]]  [[./][org/]]
- [[./output_hub.org][output_hub]]

* latex
** _module template_ :latex:pdf:module:
*** latex.d module & templates

#+HEADER: :tangle "../src/doc_reform/io_out/latex.d"
#+HEADER: :noweb yes
#+BEGIN_SRC d
<<doc_header_including_copyright_and_license>>
module doc_reform.io_out.latex;
<<Template_paper_latex>>
<<Template_output_latex>>
<<Template_latex_init>>
<<Template_latex_sty_static>>
<<Template_latex_sty_paper_dimensions>>
#+END_SRC

*** template paperLaTeX

#+NAME: Template_paper_latex
#+HEADER: :noweb yes
#+BEGIN_SRC d
template paperLaTeX() {
  import
    std.format,
    std.conv : to;
  auto paperLaTeX() {
    <<Struct_shared_geometry_paper_dimensions>>
    return PaperType();
  }
}
#+END_SRC

*** template outputLaTeX

#+NAME: Template_output_latex
#+HEADER: :noweb yes
#+BEGIN_SRC d
template outputLaTeX() {
  <<ImportsAndMixins_imports>>
  <<Function_shared_special_characters_to_escape_operations>>
  <<Function_shared_special_characters_to_escape_object>>
  <<Function_shared_special_characters_to_escape_text>>
  <<Function_shared_marked_linebreaks_newline_to_latex>>
  <<Function_shared_fontface>>
  <<Function_shared_leading_hardspaces>>
  <<Function_shared_character_nbsp_to_hardspace>>
  <<Function_shared_character_spaces_to_hardspace>>
  <<Function_shared_character_nbsp_to_space>>
  <<Function_shared_links_and_images>>
  <<Function_shared_footnotes>>
  <<Function_shared_footnotes_remove>>
  <<Function_shared_para>>
  <<Function_shared_bookindex>>
<<Function_shared_heading>>
<<Function_shared_group>>
<<Function_shared_block>>
<<Function_shared_verse>>
<<Function_shared_codeblock>>
  <<Function_shared_tablarize>>
<<Function_shared_table>>
  <<Function_head_bullets_and_indentation>>
  <<MethodOpen_head>>
    <<Struct_head_papertype>>
    <<Function_head_footer>>
    <<Struct_head_tex_papermargins>>
    <<Struct_head_tex_columns_multi>>
    <<VarSet_head_tex_columns_multi_landscape>>
    <<Struct_head_tex_colorlinks>>
    <<FmtTxtOpen_head_tex_colorlinks_mono>>
  <<FmtTxtSet_head_tex_colorlinks_mono>>
<<FmtTxtClose_head_tex_colorlinks_mono>>
    <<FmtTxtOpen_head_tex_colorlinks_color>>
  <<FmtTxtSet_head_tex_colorlinks_color>>
<<FmtTxtClose_head_tex_colorlinks_color>>
    <<MethodOpen_head_format_string_paper_set>>
      <<Condition_FmtTxtOpen_head_format_string_paper_set_format_portrait>>
<<FmtTxtSet_head_format_string_paper_set_format_portrait_tex>>
<<FmtTxtSet_head_tex_set_part_section_subsection_subsubsection_paragraph_subparagraph>>
        <<FmtTxtClose_head_format_string_paper_set_format_portrait_variables>>
      <<Condition_FmtTxtOpen_head_format_string_paper_set_format_landscape>>
<<FmtTxtSet_head_format_string_paper_set_format_landscape_tex>>
<<FmtTxtSet_head_tex_set_part_section_subsection_subsubsection_paragraph_subparagraph>>
        <<FmtTxtClose_head_format_string_paper_set_format_landscape_variables>>
    <<MethodClose_head_format_string_paper_set_return>>
    <<Switch_head_format_string_paper_set_orientation>>
    <<ConditionalSetVar_head_format_string_paper_set_color>>
    <<FmtTxtOpen_head_format_tex_set_start_latex_head>>
<<FmtTxtSet_head_tex_set_generated_by>>
<<FmtTxtSet_head_tex_set_paper_type>>
<<FmtTxtSet_head_tex_set_orintation>>
    <<FmtTxtClose_head_a_format_string_variables>>
  <<MethodClose_head_close>>
  <<MethodOpen_body_function>>
    <<MethodLoopOpen_body_foreach_doc_part>>
        <<CasePart_body_for_doc_frontmatter>>
        <<CasePart_body_for_doc_body>>
        <<CasePart_body_for_doc_backmatter>>
    <<CasePart_body_for_doc_default>>
  <<MethodClose_body_function_return>>
  <<MethodOpen_tail_function>>
    <<FmtTxtOpen_tail_format_string>>
<<FmtTxtSet_tail_format_string_tex>>
  <<FmtTxtClose_tail_format_string>>
  <<MethodClose_tail_function_return>>
  <<Function_output_write>>
  <<Function_output_set>>
}
#+END_SRC

*** template outputLaTeXstyInit

#+NAME: Template_latex_init
#+HEADER: :noweb yes
#+BEGIN_SRC d
template outputLaTeXstyInit() {
  import doc_reform.io_out;
  auto paper = paperLaTeX;
  <<Function_output_style_write>>
  <<Function_output_stylesheets_get_each_written>>
}
#+END_SRC

*** template outputLaTeXstyStatic

#+NAME: Template_latex_sty_static
#+HEADER: :noweb yes
#+BEGIN_SRC d
template outputLaTeXstyStatic() {
  <<Initialize_output_style>>
  string outputLaTeXstyStatic(
    bool   generated_by,
    string name_version_and_compiler,
    string time_output_generated,
  ) {
<<FmtTxt_output_style_static_set>>
    return latex_sty;
  }
}
#+END_SRC

*** template outputLaTeXstyPaperSizeAndOrientation

#+NAME: Template_latex_sty_paper_dimensions
#+HEADER: :noweb yes
#+BEGIN_SRC d
template outputLaTeXstyPaperSizeAndOrientation() {
  <<Initialize_output_style>>
  auto outputLaTeXstyPaperSizeAndOrientation(P)(
    P      doc_sty_info,
    bool   generated_by,
    string name_version_and_compiler,
    string time_output_generated,
  ) {
<<FmtTxt_output_style_paper_dimensions_set>>
    return latex_sty;
  }
}
#+END_SRC

** write latex output :latex:out:
*** write latex output :latex:out:

#+NAME: Function_output_write
#+BEGIN_SRC d
void writeOutputLaTeX(T,M)(
  const T      latex_content,
        M      doc_matters,
        string paper_size_orientation,
) {
  auto pth_latex = spinePathsLaTeX(doc_matters);
  try {
    { /+ debug +/
      if (doc_matters.opt.action.debug_do_latex
      && doc_matters.opt.action.vox_gt1) {
        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.vox_gt0) {
      writeln(" ", pth_latex.latex_file_with_path(paper_size_orientation));
    }
    {
      auto f = File(pth_latex.latex_file_with_path(paper_size_orientation), "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);
        }
      }
    }
    if (!exists(pth_latex.latex_path_stuff ~ "/index.html")) {
      import doc_reform.io_out.html_snippet;
      mixin htmlSnippet;
      auto f = File(pth_latex.latex_path_stuff ~"/index.html", "w");
      f.writeln(format_html_blank_page_guide_home(
        "../../css/html_scroll.css",
        (doc_matters.opt.action.webserver_url_doc_root.length > 0)
          ? doc_matters.opt.action.webserver_url_doc_root
          : doc_matters.conf_make_meta.conf.w_srv_data_root_url
          ,
        "../../index.html",
      ));
    }
    // should be in latex init and done just once, doc_matters not passed there though
    if (!exists(pth_latex.base ~ "/index.html")) {
      import doc_reform.io_out.html_snippet;
      mixin htmlSnippet;
      auto f = File(pth_latex.base ~"/index.html", "w");
      f.writeln(format_html_blank_page_guide_home(
        "../css/html_scroll.css",
        (doc_matters.opt.action.webserver_url_doc_root.length > 0)
          ? doc_matters.opt.action.webserver_url_doc_root
          : doc_matters.conf_make_meta.conf.w_srv_data_root_url,
        "../index.html",
      ));
    }
    if (!exists(pth_latex.base_sty ~ "/index.html")) {
      import doc_reform.io_out.html_snippet;
      mixin htmlSnippet;
      auto f = File(pth_latex.base_sty ~"/index.html", "w");
      f.writeln(format_html_blank_page_guide_home(
        "../../css/html_scroll.css",
        (doc_matters.opt.action.webserver_url_doc_root.length > 0)
          ? doc_matters.opt.action.webserver_url_doc_root
          : doc_matters.conf_make_meta.conf.w_srv_data_root_url,
        "../../index.html",
      ));
    }
  } catch (ErrnoException ex) {
    // handle error
  }
}
#+END_SRC

*** latex output hub [#A] :latex:pdf:out:

#+NAME: Function_output_set
#+BEGIN_SRC d
void outputLaTeX(D,M)(
  const    D   doc_abstraction,
           M   doc_matters,
) {
  struct LaTeX {
    string head;
    string content;
    string tail;
  }
  auto latex             = LaTeX();
  foreach (paper_size_orientation; doc_matters.conf_make_meta.conf.set_papersize) {
    latex.head           = latex_head(doc_matters, paper_size_orientation);
    latex.content        = latex_body(doc_abstraction, doc_matters, paper_size_orientation);
    latex.tail           = latex_tail(doc_matters, paper_size_orientation);
    latex.writeOutputLaTeX(doc_matters, paper_size_orientation);
  }
}
#+END_SRC

*** styles :latex:out:
**** initialize

#+NAME: Initialize_output_style
#+BEGIN_SRC d
import
  std.format,
  std.conv : to;
#+END_SRC

**** write latex styles output :latex:out:
***** write

#+NAME: Function_output_style_write
#+BEGIN_SRC d
void writeOutputLaTeXstyStatic(
  string latex_sty,
  string output_dir,
  string filename,
) {
  if ((output_dir.length > 0)
    && isValidPath(output_dir)
  ) {
    auto pth_latex = spinePathsLaTeXsty(output_dir);
    try {
      import std.file;
      if (!exists(pth_latex.base_sty)) {
        (pth_latex.base_sty).mkdirRecurse;
      }
      {
        auto f = File(pth_latex.latex_document_header_sty(filename), "w");
        f.writeln(latex_sty);
      }
    } catch (ErrnoException ex) {
      // handle error
    }
  }
}
#+END_SRC

***** request write (send to be written)

#+NAME: Function_output_stylesheets_get_each_written
#+BEGIN_SRC d
void outputLaTeXstyInit()(
  string output_dir,
  bool   generated_by,
  string name_version_and_compiler,
  string time_output_generated,
) {
  string latex_sty = outputLaTeXstyStatic!()(generated_by, name_version_and_compiler, time_output_generated);
  latex_sty.writeOutputLaTeXstyStatic(output_dir, "spineShared.sty");
  auto sty_a4p      = paper.a4.portrait;
  auto latex_papersize_and_orientation = outputLaTeXstyPaperSizeAndOrientation!()(sty_a4p, generated_by, name_version_and_compiler, time_output_generated);
  latex_papersize_and_orientation.writeOutputLaTeXstyStatic(output_dir, sty_a4p.stylesheet ~ ".sty");
  auto sty_a4l      = paper.a4.landscape;
  latex_papersize_and_orientation = outputLaTeXstyPaperSizeAndOrientation!()(sty_a4l, generated_by, name_version_and_compiler, time_output_generated);
  latex_papersize_and_orientation.writeOutputLaTeXstyStatic(output_dir, sty_a4l.stylesheet ~ ".sty");
  auto sty_b4p      = paper.b4.portrait;
  latex_papersize_and_orientation = outputLaTeXstyPaperSizeAndOrientation!()(sty_b4p, generated_by, name_version_and_compiler, time_output_generated);
  latex_papersize_and_orientation.writeOutputLaTeXstyStatic(output_dir, sty_b4p.stylesheet ~ ".sty");
  auto sty_b4l      = paper.b4.landscape;
  latex_papersize_and_orientation = outputLaTeXstyPaperSizeAndOrientation!()(sty_b4l, generated_by, name_version_and_compiler, time_output_generated);
  latex_papersize_and_orientation.writeOutputLaTeXstyStatic(output_dir, sty_b4l.stylesheet ~ ".sty");
  auto sty_a5p      = paper.a5.portrait;
  latex_papersize_and_orientation = outputLaTeXstyPaperSizeAndOrientation!()(sty_a5p, generated_by, name_version_and_compiler, time_output_generated);
  latex_papersize_and_orientation.writeOutputLaTeXstyStatic(output_dir, sty_a5p.stylesheet ~ ".sty");
  auto sty_a5l      = paper.a5.landscape;
  latex_papersize_and_orientation = outputLaTeXstyPaperSizeAndOrientation!()(sty_a5l, generated_by, name_version_and_compiler, time_output_generated);
  latex_papersize_and_orientation.writeOutputLaTeXstyStatic(output_dir, sty_a5l.stylesheet ~ ".sty");
  auto sty_letter_p  = paper.letter.portrait;
  latex_papersize_and_orientation = outputLaTeXstyPaperSizeAndOrientation!()(sty_letter_p, generated_by, name_version_and_compiler, time_output_generated);
  latex_papersize_and_orientation.writeOutputLaTeXstyStatic(output_dir, sty_letter_p.stylesheet ~ ".sty");
  auto sty_letter_l  = paper.letter.landscape;
  latex_papersize_and_orientation = outputLaTeXstyPaperSizeAndOrientation!()(sty_letter_l, generated_by, name_version_and_compiler, time_output_generated);
  latex_papersize_and_orientation.writeOutputLaTeXstyStatic(output_dir, sty_letter_l.stylesheet ~ ".sty");
  auto sty_legal_p   = paper.legal.portrait;
  latex_papersize_and_orientation = outputLaTeXstyPaperSizeAndOrientation!()(sty_legal_p, generated_by, name_version_and_compiler, time_output_generated);
  latex_papersize_and_orientation.writeOutputLaTeXstyStatic(output_dir, sty_legal_p.stylesheet ~ ".sty");
  auto sty_legal_l   = paper.legal.landscape;
  latex_papersize_and_orientation = outputLaTeXstyPaperSizeAndOrientation!()(sty_legal_l, generated_by, name_version_and_compiler, time_output_generated);
  latex_papersize_and_orientation.writeOutputLaTeXstyStatic(output_dir, sty_legal_l.stylesheet ~ ".sty");
}
#+END_SRC

**** sty: paper dimensions sty (calls static sty)

#+NAME: FmtTxt_output_style_paper_dimensions_set
#+BEGIN_SRC d
    string latex_sty = format(q"┃%%%% spine LaTeX output%s%s
%% - called by .tex document to set paper dimensions (size and orientation)
%% - calls spineShared.sty used/shared by all spine documents
\ProvidesPackage{./sty/%s}
\usepackage{geometry}
\geometry{
  %s,
  %s,
  left=%s,
  right=%s,
  top=%s,
  bottom=%s,
}
\usepackage{./sty/spineShared}┃",
  generated_by ? " " ~ name_version_and_compiler : "",
  generated_by ? " (generated " ~  time_output_generated ~ ")" : "",
  doc_sty_info.stylesheet,
  doc_sty_info.papersize,
  doc_sty_info.orient,
  doc_sty_info.margin_left,
  doc_sty_info.margin_right,
  doc_sty_info.margin_top,
  doc_sty_info.margin_bottom,
);
#+END_SRC

**** sty: in common

fonts to try:

#+BEGIN_SRC latex
\usepackage[scaled]{dejavu}
\renewcommand*\familydefault{\sfdefault}
\usepackage{inconsolata}
\usepackage[T1]{fontenc}
#+END_SRC

#+BEGIN_SRC latex
\usepackage[scaled]{dejavu}
\usepackage[scaled]{tgheros}
\usepackage[scaled]{quattrocento}
\usepackage[scaled]{cmbright}
\usepackage[scaled]{tgadventor}
\usepackage[scaled]{bookman}
\usepackage[scaled]{libertine}
\usepackage[scaled]{bera}
\usepackage[scale=.95,type1]{arev}
\usepackage[scale=.95,type1]{cabin}
#+END_SRC

#+NAME: FmtTxt_output_style_static_set
#+BEGIN_SRC latex
    string latex_sty = format(q"┃%%%% spine LaTeX output%s%s
%% - called by the .sty containing the paper dimensions (size and orientation) to be used
%% - spineShared.sty used by all spine documents (called indirectly)
\ProvidesPackage{./sty/spineShared}
\usepackage{multicol}
\setlength{\marginparsep}{4mm}
\setlength{\marginparwidth}{8mm}
\usepackage[scaled]{dejavu}
\renewcommand*\familydefault{\sfdefault}
\usepackage{inconsolata}
\usepackage[T1]{fontenc}
\usepackage{newunicodechar}
%% \usepackage[utf8]{inputenc}
\usepackage{alltt}
\usepackage[
  unicode=true,
	pdfusetitle,
  pdfsubject={},
  pdfkeywords={},         %% keywords list {} {} {},
  pdftoolbar=true,
  pdfmenubar=true,
  pdfwindowui=true,
  pdffitwindow=false,     %% window fit to page when opened
  pdfstartview={FitH},    %% fits the width of the page to the window
  pdfnewwindow=true,      %% links in new window
  pdfborder={0 0 1},
  plainpages=false,       %% was true
  bookmarks=true,
  bookmarksopen=false,
  bookmarksnumbered=false,
  backref=false,
  breaklinks=false,
  colorlinks=true,
  urlcolor=black,
  filecolor=black,
  linkcolor=black,
  citecolor=black,        %% links_mono_or_color_set
]{hyperref}
\PassOptionsToPackage{hyphens}{url}\usepackage{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{textcomp}
\usepackage[parfill]{parskip}
\usepackage[normalem]{ulem}
\usepackage{soul}
\usepackage{longtable}
\usepackage{graphicx}
\usepackage[tc]{titlepic}
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage[cm]{sfmath}
\usepackage{underscore}
\usepackage{listings}
\setcounter{secnumdepth}{2}
\setcounter{tocdepth}{4}
\usepackage{bookmark}
\usepackage{microtype}
\makeatletter
\usepackage[multiple,ragged]{footmisc}
\setlength\footnotemargin{12pt}
\usepackage[para]{manyfoot}
\DeclareNewFootnote{A}
\makeatother
\chardef\txtbullet="2022
\chardef\tilde="7E
\def\asterisk{{\rm \char42} }
\definecolor{Light}{gray}{.92}
\definecolor{listinggray}{gray}{0.9}
\definecolor{lbcolor}{rgb}{0.9,0.9,0.9}
\lstset{
  backgroundcolor=\color{lbcolor},
  tabsize=4,
  rulecolor=,
  language=,
  basicstyle={\ttfamily\scriptsize},
  upquote=true,
  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},
}
\DeclareTOCStyleEntry[numwidth+=8pt]{part}{part}
\DeclareTOCStyleEntry[numwidth+=4pt]{section}{section}
\DeclareTOCStyleEntry[numwidth+=3pt]{section}{paragraph}
\DeclareTOCStyleEntry[numwidth+=3pt]{section}{subparagraph}
\DeclareTOCStyleEntry[numwidth+=3pt]{section}{subsection}
\DeclareTOCStyleEntries[indent+=4pt]{section}{section,subsection,subsubsection}
\DeclareTOCStyleEntries[numwidth+=3pt]{section}{paragraph,subparagraph}
\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[]
  $\txtbullet$\hspace{\enspace}
} {\end{list}}
\newcommand{\monosp}[1]{\normaltext\ttfamily\texbackslash#1}
\newcommand{\br}{\hfill\break}
\newcommand{\brl}[1]{%%
  \ifx&#1&%%
    \hfill\break
  \else
    \vspace{#1ex}
  \fi
}
\newcommand{\brln}{\hspace*{\fill}\linebreak}
\newcommand{\objBlockOpen}{%%
  \setlength{\parskip}{0.5ex plus0.2ex minus0.1ex}\raggedright
  \begin{footnotesize}
}
\newcommand{\objBlockClose}{%%
  \end{footnotesize}
  \setlength{\parskip}{1ex plus0.5ex minus0.2ex}
}
\newcommand{\objGroupOpen}{%%
  \setlength{\parskip}{0.5ex plus0.2ex minus0.1ex}
  \begin{footnotesize}
}
\newcommand{\objGroupClose}{%%
  \end{footnotesize}
}
\newcommand{\objPoemVerseOpen}{%%
  \setlength{\parskip}{0.1ex plus0.1ex minus0.1ex}
  \begin{footnotesize}

}
\newcommand{\objPoemVerseClose}{%%

  \end{footnotesize}
  \setlength{\parskip}{1ex plus0.5ex minus0.2ex}
  \linebreak
}
\newcommand{\parasep}{%%
  \smallskip \begin{center}*\hspace{2em}*\hspace{2em}*\end{center} \br
}
\newcommand{\spaces}[1]{{\hspace*{#1ex}}}
\newcommand{\s}{\hspace*{1ex}}
\newcommand{\hardspace}{\hspace*{1ex}}
\newcommand{\-}{\hspace*{1ex}}
\newcommand{\caret}{{\^{~}}}
\newcommand{\pipe}{{\textbar}}
\newcommand{\curlyOpen}{{}
\newcommand{\curlyClose}{}}
\newcommand{\lt}{{UseTextSymbol{OML}{<}}}
\newcommand{\gt}{{UseTextSymbol{OML}{>}}}
\newcommand{\slash}{{/}}
\newcommand{\underscore}{\_}
\newcommand{\exclaim}{\Verbatim{!}}
\newcommand{\linktext}[2]{%%
  {\href{#1}
  {\;\ulcorner\,\textup{{#2}}\,\lrcorner}}
}
\newcommand{\linkurl}[2]{%%
  \;{\href{#1}
  {\;\scriptsize\ttfamily\ulcorner\,\textup{{#2}}\,\lrcorner}}
}
\newcommand{\link}[2]{%%
  {\begin{scriptsize}\color{black}\urlstyle{tt}\href{#1}
  {\;\ulcorner\,{#2}\,\lrcorner}\end{scriptsize}}
}
\newcommand{\objCodeBlock}[1]{\normaltext\raggedright\small\ttfamily\texbackslash#1}
\newcommand{\objCodeOpen}{%%
  \normaltext\raggedright\small\ttfamily\texbackslash
  \begin{lstlisting}
}
\newcommand{\objCodeClose}{%%
  \end{lstlisting}
}
\newcommand{\ocn}[1]{%%
  \setlength{\parindent}{0em}
  \ifx&#1&%% #1 is empty
    \hspace{-0.5ex}{\marginpar{\begin{tiny}\end{tiny}}}
  \else%% #1 is nonempty
    \hspace{-0.5ex}{\marginpar{\begin{tiny}\hspace{0em}\hypertarget{#1}{#1}\end{tiny}}}
  \fi
}
\newcommand{\ocnhold}[1]{%%
  \begin{tiny}\hspace{0mm}\end{tiny}{\marginpar{\begin{tiny}\hspace{0mm}\hypertarget{#1}{#1}\end{tiny}}}
}
\newcommand{\objCodeBlockHold}[1]{\normaltext\raggedright\small\ttfamily\texbackslash#1}
\newcommand{\objTableOpen}[1]{%%
  \setlength{\LTleft}{0pt}
  \setlength{\LTright}{\fill}
  \begin{tiny}
  \begin{longtable}{#1}
}
\newcommand{\objTableClose}{%%
  \end{longtable}
  \end{tiny}
}
%% \tolerance=300
%% \clubpenalty=300
%% \widowpenalty=300
%% \usepackage{atbegshi} %% http://ctan.org/pkg/atbegshi         %% (BUG tmp FIX deal with problem, remove first page which is blank)
%% \AtBeginDocument{\AtBeginShipoutNext{\AtBeginShipoutDiscard}} %% (BUG tmp FIX deal with problem, remove first page which is blank)
┃",
  generated_by ? " " ~ name_version_and_compiler : "",
  generated_by ? " (generated " ~  time_output_generated ~ ")" : "",
);
#+END_SRC

* stuff
** output imports

#+NAME: ImportsAndMixins_imports
#+BEGIN_SRC d
import
  std.digest.sha,
  std.file,
  std.outbuffer,
  std.uri,
  std.conv : to;
import
  doc_reform.io_out,
  doc_reform.io_out.rgx,
  doc_reform.io_out.rgx_latex;
mixin spineRgxOut;
static auto rgx = RgxO();
mixin spineRgxLSC;
static auto rgx_sc = RgxLSC();
mixin spineLanguageCodes;
auto lang = Lang();
auto paper = paperLaTeX;
#+END_SRC

** shared
*** paper dimensions (struct) geometry

#+NAME: Struct_shared_geometry_paper_dimensions
#+BEGIN_SRC d
string mm(uint mmi) {
  string _mm = format(q"┃%smm┃", mmi.to!string);
  return _mm;
}
struct PaperType {
  @safe auto a4() {
    struct A4 {
      auto portrait() {
        struct V {
          string       stylesheet    = "spineA4portrait";
          string       papersize     = "a4paper";
          string       orient        = "portrait";
          string       fontsize      = "11pt";
          const uint   w             = 170;
          const uint   h             = 257;
          const uint   l             = 30;
          const uint   r             = 20;
          const uint   t             = 30;
          const uint   b             = 30;
          string       width         = mm(w);
          string       height        = mm(h);
          string       margin_left   = mm(l);
          string       margin_right  = mm(r);
          string       margin_top    = mm(t);
          string       margin_bottom = mm(b);
          uint         img_px        = 450;
          bool         is_portrait   = true;
        }
        return V();
      }
      auto landscape() {
        struct H {
          string       stylesheet    = "spineA4landscape";
          string       papersize     = "a4paper";
          string       orient        = "landscape";
          string       fontsize      = "11pt";
          const uint   w             = 238;
          const uint   h             = 160;
          const uint   l             = 30;
          const uint   r             = 20;
          const uint   t             = 30;
          const uint   b             = 30;
          string       width         = mm(w);
          string       height        = mm(h);
          string       margin_left   = mm(l);
          string       margin_right  = mm(r);
          string       margin_top    = mm(t);
          string       margin_bottom = mm(b);
          uint         img_px        = 300;
          bool         is_portrait   = false;
        }
        return H();
      }
    }
    return A4();
  }
  @safe auto a5() {
    struct A5 {
      auto portrait() {
        struct V {
          string       stylesheet    = "spineA5portrait";
          string       papersize     = "a5paper";
          string       orient        = "portrait";
          string       fontsize      = "11pt";
          const uint   w             = 112;
          const uint   h             = 162;
          const uint   l             = 30;
          const uint   r             = 20;
          const uint   t             = 30;
          const uint   b             = 30;
          string       width         = mm(w);
          string       height        = mm(h);
          string       margin_left   = mm(l);
          string       margin_right  = mm(r);
          string       margin_top    = mm(t);
          string       margin_bottom = mm(b);
          uint         img_px        = 280;
          bool         is_portrait   = true;
        }
        return V();
      }
      auto landscape() {
        struct H {
          string       stylesheet    = "spineA5landscape";
          string       papersize     = "a5paper";
          string       orient        = "landscape";
          string       fontsize      = "11pt";
          const uint   w             = 152;
          const uint   h             = 100;
          const uint   l             = 30;
          const uint   r             = 20;
          const uint   t             = 30;
          const uint   b             = 30;
          string       width         = mm(w);
          string       height        = mm(h);
          string       margin_left   = mm(l);
          string       margin_right  = mm(r);
          string       margin_top    = mm(t);
          string       margin_bottom = mm(b);
          uint         img_px        = 190;
          bool         is_portrait   = false;
        }
        return H();
      }
    }
    return A5();
  }
  @safe auto b4() {
    struct B4 {
      auto portrait() {
        struct V {
          string       stylesheet    = "spineB4portrait";
          string       papersize     = "b4paper";
          string       orient        = "portrait";
          string       fontsize      = "11pt";
          const uint   w             = 140;
          const uint   h             = 204;
          const uint   l             = 30;
          const uint   r             = 20;
          const uint   t             = 30;
          const uint   b             = 30;
          string       width         = mm(w);
          string       height        = mm(h);
          string       margin_left   = mm(l);
          string       margin_right  = mm(r);
          string       margin_top    = mm(t);
          string       margin_bottom = mm(b);
          uint         img_px        = 356;
          bool         is_portrait   = true;
        }
        return V();
      }
      auto landscape() {
        struct H {
          string       stylesheet    = "spineB4landsape";
          string       papersize     = "b4paper";
          string       orient        = "landscape";
          string       fontsize      = "11pt";
          const uint   w             = 200;
          const uint   h             = 130;
          const uint   l             = 30;
          const uint   r             = 20;
          const uint   t             = 30;
          const uint   b             = 30;
          string       width         = mm(w);
          string       height        = mm(h);
          string       margin_left   = mm(l);
          string       margin_right  = mm(r);
          string       margin_top    = mm(t);
          string       margin_bottom = mm(b);
          uint         img_px        = 260;
          bool         is_portrait   = false;
        }
        return H();
      }
    }
    return B4();
  }
  @safe auto letter() {
    struct Letter {
      auto portrait() {
        struct V {
          string       stylesheet    = "spineLetterPortrait";
          string       papersize     = "letterpaper";
          string       orient        = "portrait";
          string       fontsize      = "11pt";
          const uint   w             = 166;
          const uint   h             = 212;
          const uint   l             = 30;
          const uint   r             = 20;
          const uint   t             = 30;
          const uint   b             = 30;
          string       width         = mm(w);
          string       height        = mm(h);
          string       margin_left   = mm(l);
          string       margin_right  = mm(r);
          string       margin_top    = mm(t);
          string       margin_bottom = mm(b);
          uint         img_px        = 468;
          bool         is_portrait   = true;
        }
        return V();
      }
      auto landscape() {
        struct H {
          string       stylesheet    = "spineLetterLandscape";
          string       papersize     = "letterpaper";
          string       orient        = "landscape";
          string       fontsize      = "11pt";
          const uint   w             = 226;
          const uint   h             = 166;
          const uint   l             = 30;
          const uint   r             = 20;
          const uint   t             = 30;
          const uint   b             = 30;
          string       width         = mm(w);
          string       height        = mm(h);
          string       margin_left   = mm(l);
          string       margin_right  = mm(r);
          string       margin_top    = mm(t);
          string       margin_bottom = mm(b);
          uint         img_px        = 290;
          bool         is_portrait   = false;
        }
        return H();
      }
    }
    return Letter();
  }
  @safe auto legal() {
    struct Legal {
      auto portrait() {
        struct V {
          string       stylesheet    = "spineLegalPortrait";
          string       papersize     = "legalpaper";
          string       orient        = "portrait";
          string       fontsize      = "11pt";
          const uint   w             = 168;
          const uint   h             = 286;
          const uint   l             = 30;
          const uint   r             = 20;
          const uint   t             = 30;
          const uint   b             = 30;
          string       width         = mm(w);
          string       height        = mm(h);
          string       margin_left   = mm(l);
          string       margin_right  = mm(r);
          string       margin_top    = mm(t);
          string       margin_bottom = mm(b);
          uint         img_px        = 474;
          bool         is_portrait   = true;
        }
        return V();
      }
      auto landscape() {
        struct H {
          string       stylesheet    = "spineLegalLandscape";
          string       papersize     = "legalpaper";
          string       orient        = "landscape";
          string       fontsize      = "11pt";
          const uint   w             = 296;
          const uint   h             = 166;
          const uint   l             = 30;
          const uint   r             = 20;
          const uint   t             = 30;
          const uint   b             = 30;
          string       width         = mm(w);
          string       height        = mm(h);
          string       margin_left   = mm(l);
          string       margin_right  = mm(r);
          string       margin_top    = mm(t);
          string       margin_bottom = mm(b);
          uint         img_px        = 420;
          bool         is_portrait   = false;
        }
        return H();
      }
    }
    return Legal();
  }
}
#+END_SRC

*** latex \escape special characters
**** object (string text within)

- “” characters not directly available for use in font
- run into problems using " for quotes and modified latex header
  - (m => "\"")
  - \usepackage[autostyle, english = american]{csquotes}
    \MakeOuterQuote{"}
- (m => "''") works alone better

#+NAME: Function_shared_special_characters_to_escape_operations
#+BEGIN_SRC d
@safe string sp_char_ops()(
  string      _txt,
) {
  string _unescape_sp_char_esc()(string _txt) {
    _txt = _txt
      .replaceAll(rgx_sc.latex_special_char_escaped,
        format(q"┃%s┃", "$1"))
      .replaceAll(rgx_sc.latex_special_char_escaped_braced,
        format(q"┃%s┃", "$1"));
    return _txt;
  }
  string _unescape_fontface_esc()(string _txt) {
    _txt = _txt.replaceAll(rgx_sc.latex_identify_inline_fontface,
         format(q"┃%s%s┃", "$1", "$2"));
    return _txt;
  }
  _txt = replaceAll!(m => "\\" ~ m[1])(_txt, rgx_sc.latex_special_char_for_escape);
  _txt = replaceAll!(m => "{\\" ~ m[1] ~ "}")(_txt, rgx_sc.latex_special_char_for_escape_and_braces);
  _txt = replaceAll!(m => "''")(_txt, rgx.quotes_open_and_close);
  _txt = replaceAll!(m => "$\\cdot$")(_txt, rgx.middle_dot);
  _txt = replaceAll!(m => _unescape_sp_char_esc(m[0]))(_txt, rgx_sc.latex_identify_inline_link);
  _txt = replaceAll!(m => _unescape_fontface_esc(m[0]))(_txt, rgx_sc.latex_identify_inline_fontface);
  return _txt;
}
#+END_SRC

#+NAME: Function_shared_special_characters_to_escape_object
#+BEGIN_SRC d
@safe string sp_char_esc(O)(
  string      _txt,
  const    O  obj,
) {
  if (obj.metainfo.is_a != "code") {
    _txt = _txt.sp_char_ops;
  }
  return _txt;
}
#+END_SRC

**** string text

#+NAME: Function_shared_special_characters_to_escape_text
#+BEGIN_SRC d
@safe string sp_char_esc_txt()(
  string      _txt,
) {
  _txt = _txt.sp_char_ops;
  return _txt;
}
#+END_SRC

***** line breaks

#+NAME: Function_shared_marked_linebreaks_newline_to_latex
#+BEGIN_SRC d
@safe string marked_linebreaks_newlines()(
  string      _txt,
) {
  _txt = _txt.split(rgx.br_linebreaks_newlines).join("\\br\n").strip;
  // _txt = replaceAll!(m => "\\br " ~ m[1])(_txt, rgx.br_linebreaks_newlines);
  return _txt;
}
#+END_SRC

*** not used latex \escape special characters UNUSED

#+BEGIN_SRC d
@safe string sp_char_esc_()(
  string      _txt,
) {
  _txt = replaceAll!(m => "\\" ~ m[1])(_txt, rgx_sc.latex_special_char);
  return _txt;
}
#+END_SRC

*** inline markup
**** fontface

- bold, italics, underscore, strikethrough

#+NAME: Function_shared_fontface
#+BEGIN_SRC d
@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"┃$$^{%s}$$┃",           "$1"))
  .replaceAll(rgx.inline_subscript,   format(q"┃$$_{%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;
}
#+END_SRC

**** spaces
***** leading hardspace UNUSED

#+NAME: Function_shared_leading_hardspaces
#+BEGIN_SRC d
@safe string leading_hardspaces()(
  string      _txt,
) {
  string hardspaces(string _spaces) {
    _spaces  = _spaces
      .replaceAll(rgx.space, "{\\s}");
    return _spaces;
  }
  _txt = replaceAll!(m => hardspaces(m[0]))(_txt, rgx.spaces_line_start);
  return _txt;
}
#+END_SRC

***** nbsp character UNUSED

#+NAME: output_latex_shared_character_nbsp_to_hardspace_
#+BEGIN_SRC d
@safe string nbsp_char_replace()(string _txt) {
  if (_txt.match(rgx.nbsp_char)) {
    _txt = _txt.replaceAll(rgx.nbsp_char, "{\\s}");
  }
  return _txt;
}
#+END_SRC

***** nbsp character

#+NAME: Function_shared_character_nbsp_to_hardspace
#+BEGIN_SRC d
@safe string nbsp_char()(string _txt) {
  if (_txt.match(rgx.nbsp_char)) {
    foreach (m; _txt.matchAll(rgx.nbsp_chars)) {
      int spaces_ = 0;
      foreach (n; m[0].matchAll(rgx.nbsp_char)) {
        spaces_ ++;
      }
      _txt = _txt.replaceFirst(rgx.nbsp_chars, "\\spaces{" ~ spaces_.to!string ~ "}");
    }
  }
  return _txt;
}
#+END_SRC

***** keep spaces

#+NAME: Function_shared_character_spaces_to_hardspace
#+BEGIN_SRC d
@safe string spaces_to_nbsp()(string _txt) {
  if (_txt.match(rgx.spaces_keep)) {
    foreach (m; _txt.matchAll(rgx.spaces_keep)) {
      int spaces_ = 0;
      foreach (n; m[0].matchAll(rgx.space)) {
        spaces_ ++;
      }
      _txt = _txt.replaceFirst(rgx.spaces_keep, "\\spaces{" ~ spaces_.to!string ~ "}");
    }
  }
  return _txt;
}
#+END_SRC

***** remove nbsp character

#+NAME: Function_shared_character_nbsp_to_space
#+BEGIN_SRC d
@safe string nbsp_char_to_space()(string _txt) {
  if (_txt.match(rgx.nbsp_char)) {
    _txt  = _txt.replaceAll(rgx.nbsp_char, " ");
  }
  return _txt;
}
#+END_SRC

**** links and images
***** links / urls

#+NAME: Function_shared_links_and_images
#+BEGIN_SRC d
@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]), " \\br\n")
          )(_linked_content, rgx.inline_image_info);
      }
      return _linked_content;
    }
    string _check_link(string _link) {
      _link = _link
        .replaceFirst(rgx_sc.latex_clean_internal_link, "")
        .replaceAll(rgx_sc.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))) // url link (regular link with url)
          ? format(q"┃\linkurl{%s}{%s}┃", _check_link(m[1]), (_check_link(m[1])).sp_char_esc_txt)
          : ((m[2].match(rgx.uri)) && (m[1].match(rgx.inline_image_info))) // linked image
            ? format(q"┃%s\href{%s}%s{%s}┃", "\\br ", _check_link(m[2]), "\n", _if_images(m[1])) // markup for images
            : (m[2].match(rgx.uri)) // not linked image
              ? format(q"┃%s\linktext{%s}{%s}┃", "\\br ", _check_link(m[2]), m[1]) // regular link with text
              : format(q"┃\hyperlink{%s}{%s}┃", _check_link(m[2]), _if_images(m[1])) // internal links, like book index
        )(_txt, rgx.inline_link);
    }
  }
  return _txt;
}
#+END_SRC

*** footnotes
**** footnotes

#+NAME: Function_shared_footnotes
#+BEGIN_SRC d
@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.split(rgx.br_linebreaks).join("\\br ").replaceAll(rgx.inline_notes_al_regular_number_note,
      format(_tex_note,
        "$1", "$1", "$1",
        "$2".strip
      ).strip
    );
  }
  return _txt;
}
#+END_SRC

**** footnote remove

#+NAME: Function_shared_footnotes_remove
#+BEGIN_SRC d
@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;
}
#+END_SRC

*** para
**** para

#+NAME: Function_shared_para
#+BEGIN_SRC d
@safe string para(O)(
  string      _txt,
  O           obj,
) {
  if (obj.metainfo.is_of_type == "para") {
    string _tex_para;
    _tex_para = q"┃\ocn{%s}%s┃";
    _txt  = format(_tex_para,
      obj.metainfo.object_number,
      _txt.footnotes
    ).strip;
  }
  return _txt;
}
#+END_SRC

**** bookindex para

#+NAME: Function_shared_bookindex
#+BEGIN_SRC d
@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_sc.latex_clean_bookindex_linebreak, "\n") ~ "\n\\brln\n"
    );
  }
  return _txt;
}
#+END_SRC

*** bullets & indentation

#+NAME: Function_head_bullets_and_indentation
#+BEGIN_SRC d
@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}%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.split(rgx.br_linebreaks_newlines).join("\\br\n")
    ).strip;
  }
  return _txt;
}
#+END_SRC

*** heading

#+NAME: Function_shared_heading
#+BEGIN_SRC d
  @safe string heading(O,M)(
    string      _txt,
    O           obj,
    M           doc_matters,
    string      paper_size_orientation,
    string      _part = ""
  ) {
    struct latexMarks {
      string pg_break = "\\clearpage\n";
    }
    latexMarks manual_breaks(
      latexMarks _ltx,
      string     test_for_break_level,
    ) {
      if ((!(doc_matters.conf_make_meta.make.breaks.empty)
        && (matchFirst(doc_matters.conf_make_meta.make.breaks, test_for_break_level)))
      ) {                                                                      // manually override defaults
        if ((matchFirst(doc_matters.conf_make_meta.make.breaks, rgx.make_breakpage))
          && (matchFirst(doc_matters.conf_make_meta.make.breaks, rgx.make_breakcolumn))
        ) {
          if (auto m = matchFirst(doc_matters.conf_make_meta.make.breaks, rgx.make_breakpage)) {
            if (matchFirst(m.captures["breakpage"], test_for_break_level)) {
              _ltx.pg_break = "\\clearpage\n";
            } else if (auto n = matchFirst(doc_matters.conf_make_meta.make.breaks, rgx.make_breakcolumn)) {
              if (matchFirst(n.captures["breakcolumn"], test_for_break_level)) {
                if ((paper_size_orientation == "a4.landscape")
                  || (paper_size_orientation == "b4.landscape")
                  || (paper_size_orientation == "a5.landscape")
                  || (paper_size_orientation == "letter.landscape")
                  || (paper_size_orientation == "legal.landscape")
                ) {
                  _ltx.pg_break = "\\\\ \\columnbreak\n"; // "\\\\ \\newpage\n";
                } else { // portrait
                  _ltx.pg_break = "\\clearpage\n";
                }
              }
            }
          }
        } else if (auto m = matchFirst(doc_matters.conf_make_meta.make.breaks, rgx.make_breakpage)) {
          if (matchFirst(m.captures["breakpage"], test_for_break_level)) {
            _ltx.pg_break = "\\clearpage\n";
          }
        } else if (auto m = matchFirst(doc_matters.conf_make_meta.make.breaks, rgx.make_breakcolumn)) {
          if (matchFirst(m.captures["breakcolumn"], test_for_break_level)) {
            if ((paper_size_orientation == "a4.landscape")
              || (paper_size_orientation == "b4.landscape")
              || (paper_size_orientation == "a5.landscape")
              || (paper_size_orientation == "letter.landscape")
              || (paper_size_orientation == "legal.landscape")
            ) {
              _ltx.pg_break = "\\\\ \\columnbreak\n"; // "\\\\ \\newpage\n";
            } else { // portrait
              _ltx.pg_break = "\\clearpage\n";
            }
          }
        }
      } else if (!(doc_matters.conf_make_meta.make.breaks.empty)) {
        _ltx.pg_break = "";
      }
      return _ltx;
    }
    if (obj.metainfo.is_a == "heading") {
      string _tex_para;
      latexMarks _ltx = latexMarks();
      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\\br\n\\begin{multicols}{2}";
        if (doc_matters.conf_make_meta.make.doc_type == "article") {             // defaults for article
          _ltx.pg_break = "";
        } else if (doc_matters.conf_make_meta.make.doc_type == "book") {         // defaults for book
          _ltx.pg_break = "\\clearpage\n";
        } else {
          _ltx.pg_break = "\\clearpage\n";
        }
        _ltx = manual_breaks(_ltx, "1");
        _pg_break = _ltx.pg_break;
        _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 = " \\br\n";
        _title_add = "";
        goto default;
      case 6: // 3 == subsubsection
        _pg_break = "";
        // _pg_break = "newpage"; // doubt this is necessary
        _sect = "subsubsection";
        _post = " \\br\n";
        _title_add = "";
        goto default;
      case 7: // 4 == paragraph
        _pg_break = "";
        // _pg_break = "newpage"; // doubt this is necessary
        _sect = "paragraph";
        _post = " \\br\n";
        _title_add = "";
        goto default;
      case 8: // 5 == subparagraph
        _pg_break = "";
        // _pg_break = "newpage"; // doubt this is necessary
        _sect = "subparagraph";
        _post = " \\br\n";
        _title_add = "";
        goto default;
      default:
        if (obj.metainfo.heading_lev_markup == 0) {
          _tex_para = q"┃
\begin{document}
\thispagestyle{empty}
\title{%s%s}
\author{ \textnormal{%s}}
\date{\begin{tiny}%s\end{tiny}}
\maketitle
\addcontentsline{toc}{part}{%s}
\newpage
\pagestyle{fancy}
\pagenumbering{alph}
\setcounter{page}{1}
\markboth{%s}{%s}
\br\linebreak Copyright {\begin{small}{\copyright\end{small}} %s \br\linebreak
%s
\clearpage┃";
          _txt = format(_tex_para,
            (doc_matters.conf_make_meta.meta.title_main).sp_char_esc_txt,
            doc_matters.conf_make_meta.meta.title_subtitle.empty ? ""
            : " \\\\ - \\\\ " ~ (doc_matters.conf_make_meta.meta.title_subtitle).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_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.marked_linebreaks_newlines,
            (doc_matters.conf_make_meta.meta.rights_license).sp_char_esc_txt.marked_linebreaks_newlines,
          );
        } else if (obj.metainfo.heading_lev_markup < 4) {
          if (!(_txt.footnotes.strip == "Endnotes")) {
            _tex_para = q"┃%s\part*{\ocn{%s}%s}
\addcontentsline{toc}{part}{%s}
\markboth{%s}┃";
            _txt = format(_tex_para,
              _pg_break,
              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{arabic}
\setcounter{page}{1}
\markboth{ }{ }
\part*{\ocn{1}%s \newline %s}

\clearpage
\pagenumbering{roman}
\setcounter{page}{1}
\renewcommand{\contentsname}{}
\tableofcontents

\clearpage
\pagenumbering{arabic}
\setcounter{page}{2}
\clearpage
\markboth{%s}{%s}
%% \null
\clearpage
\setcounter{page}{2}┃";
            _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_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*{\ocn{%s}%s}
\addcontentsline{toc}{%s}{%s%s}%s%s┃";
            _txt = format(_tex_para,
              _pg_break,
              _sect.strip,
              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*{\ocn{%s}%s}
\addcontentsline{toc}{%s}{%s%s}%s┃";
            _txt = format(_tex_para,
              _pg_break,
              _sect.strip,
              obj.metainfo.object_number,
              _txt.footnotes.strip,
              _sect,
              _txt.remove_footnotes.strip,
              _post,
              _title_add,
            );
          }
        }
        break;
      }
    }
    return _txt.strip;
  }
#+END_SRC

*** grouped text
**** group

- (hardspace not honored) clear hardspace marker

#+NAME: Function_shared_group
#+BEGIN_SRC d
  string group(O,M)(
    string      _txt,
    O           obj,
    M           doc_matters,
  ) {
    if (obj.metainfo.is_a == "group") {
      string _tex_para;
      _tex_para = q"┃\ocn{%s}\objGroupOpen
%s
\objGroupClose
┃";
      _txt  = format(_tex_para,
        obj.metainfo.object_number,
        _txt.footnotes.split(rgx.br_line_spaced).join("\\brl{1}").strip // provides more control (more noise, not as tidy)
        // _txt.footnotes.split(rgx.br_line_spaced).join("") // this works using a line-space, looks tidy, keep ref.
      ).strip;
    }
    return _txt;
  }
#+END_SRC

**** block

- (hardspace honored) \hardspace

#+NAME: Function_shared_block
#+BEGIN_SRC d
  string block(O,M)(
    string      _txt,
    O           obj,
    M           doc_matters,
  ) {
    if (obj.metainfo.is_a == "block") {
      string _tex_para;
      _tex_para = q"┃\ocn{%s}\objBlockOpen
%s
\objBlockClose
┃";
      _txt = format(_tex_para,
        obj.metainfo.object_number,
        _txt.nbsp_char.footnotes.split(rgx.br_linebreaks_newlines).join("\\br\n").strip
      ).strip;
    }
    return _txt;
  }
#+END_SRC

**** (poem) verse

- (hardspace honored) \hardspace

#+NAME: Function_shared_verse
#+BEGIN_SRC d
  string verse(O,M)(
    string      _txt,
    O           obj,
    M           doc_matters,
  ) {
    if (obj.metainfo.is_a == "verse") {
      string _tex_para;
      _tex_para = q"┃\ocn{%s}\objPoemVerseOpen
%s
\objPoemVerseClose
┃";
      _txt  = format(_tex_para,
        obj.metainfo.object_number,
        _txt.spaces_to_nbsp.footnotes.split(rgx.br_linebreaks_newlines).join("\\br\n").strip
      ).strip;
    }
    return _txt;
  }
#+END_SRC

**** codeblock

- (hardspace honored) \begin{lstlisting} clear hardspace marker

#+NAME: Function_shared_codeblock
#+BEGIN_SRC d
  string codeblock(O,M)(
    string      _txt,
    O           obj,
    M           doc_matters,
  ) {
    if (obj.metainfo.is_a == "code") {
      string _tex_para;
      _tex_para = q"┃\ocn{%s}\begin{objCodeBlock}\begin{lstlisting}
%s
\end{lstlisting}\end{objCodeBlock}
┃";
      _txt  = format(_tex_para,
        obj.metainfo.object_number,
        _txt.nbsp_char_to_space
      ).strip;
    }
    return _txt;
  }
#+END_SRC

**** table

- own set of rules

***** tablarize

#+NAME: Function_shared_tablarize
#+BEGIN_SRC d
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;
}
#+END_SRC

***** table

#+NAME: Function_shared_table
#+BEGIN_SRC d
  string table(O,M)(
    string      _txt,
    O           obj,
    M           doc_matters,
    string      paper_size_orientation,
  ) {
    if (obj.metainfo.is_a == "table") {
      auto _t = _txt.tablarize(obj);
      string _table = _t[0];
      string _t_n = _t[1];
      uint pw = 0;
      switch (paper_size_orientation) {
      case "a4.portrait":      pw = (paper.a4.portrait.w      - 20); break;
      case "a4.landscape":     pw = (paper.a4.landscape.w     - 20); break;
      case "b4.portrait":      pw = (paper.b4.portrait.w      - 20); break;
      case "b4.landscape":     pw = (paper.b4.landscape.w     - 20); break;
      case "a5.portrait":      pw = (paper.a5.portrait.w      - 20); break;
      case "a5.landscape":     pw = (paper.a5.landscape.w     - 20); break;
      case "letter.portrait":  pw = (paper.letter.portrait.w  - 20); break;
      case "letter.landscape": pw = (paper.letter.landscape.w - 20); break;
      case "legal.portrait":   pw = (paper.legal.portrait.w   - 20); break;
      case "legal.landscape":  pw = (paper.legal.landscape.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"┃\ocn{%s}\objTableOpen{%s}
%s
\objTableClose
┃";
      _txt  = format(_tex_para,
        obj.metainfo.object_number,
        _colw,
        _table,
      ).strip;
    }
    return _txt;
  }
#+END_SRC

** latex parts
*** latex head :head:
**** latex head function

#+NAME: MethodOpen_head
#+BEGIN_SRC d
string latex_head(M)(
  M      doc_matters,
  string paper_size_orientation,
) {
#+END_SRC

**** latex head options
***** paper type dimensions
****** struct

#+NAME: Struct_head_papertype
#+BEGIN_SRC d
struct paperTypeLatex {
  string a4_portrait;
  string a4_landscape;
  string b4_portrait;
  string b4_landscape;
  string a5_portrait;
  string a5_landscape;
  string us_letter_portrait;
  string us_letter_landscape;
  string us_legal_portrait;
  string us_legal_landscape;
}
auto paper_type_latex           = paperTypeLatex();
#+END_SRC

****** footer

#+NAME: Function_head_footer
#+BEGIN_SRC d
string _footer(M)(M doc_matters) {
  string _ft = "\\lfoot[\\textrm{\\thepage}]";
  string _ft_1 = format(q"┃{\tiny \href{%s}{%s}}┃", "https://sisudoc.org", "SiSU",);
  string _ft_2 = format(q"┃
\cfoot{\href{%s}{%s}}┃", "https://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;
}
#+END_SRC

***** paper margins
****** struct

#+NAME: Struct_head_tex_papermargins
#+BEGIN_SRC d
struct paperMargins {
  string portrait;
  string landscape;
}
auto margins           = paperMargins();
#+END_SRC

***** multicol
****** struct

#+NAME: Struct_head_tex_columns_multi
#+BEGIN_SRC d
struct columnsMulti {
  string portrait;
  string landscape;
}
auto multicol           = columnsMulti();
#+END_SRC

****** portrait

#+NAME: FmtTxtOpen_head_tex_columns_multi_portrait
#+BEGIN_SRC d
multicol.portrait    = format(q"┃
#+END_SRC

#+NAME: FmtTxtSet_head_tex_columns_multi_portrait
#+BEGIN_SRC latex
\usepackage{multicol}
#+END_SRC

#+NAME: FmtTxtClose_head_tex_columns_multi_portrait
#+BEGIN_SRC d
┃",
    );
#+END_SRC

****** landscape

#+NAME: VarSet_head_tex_columns_multi_landscape
#+BEGIN_SRC d
multicol.landscape    = "";
#+END_SRC

***** color links
****** struct

#+NAME: Struct_head_tex_colorlinks
#+BEGIN_SRC d
struct colorLinks {
  string mono;
  string color;
}
auto links           = colorLinks();
#+END_SRC

****** mono

#+NAME: FmtTxtOpen_head_tex_colorlinks_mono
#+BEGIN_SRC d
links.mono    = format(q"┃
#+END_SRC

#+NAME: FmtTxtSet_head_tex_colorlinks_mono
#+BEGIN_SRC latex
colorlinks=true,
urlcolor=black,
filecolor=black,
linkcolor=black,
citecolor=black,
#+END_SRC

#+NAME: FmtTxtClose_head_tex_colorlinks_mono
#+BEGIN_SRC d
┃",
    );
#+END_SRC

****** color

#+NAME: FmtTxtOpen_head_tex_colorlinks_color
#+BEGIN_SRC d
links.color    = format(q"┃
#+END_SRC

#+NAME: FmtTxtSet_head_tex_colorlinks_color
#+BEGIN_SRC latex
colorlinks=true,
urlcolor=myblue,    %% \href{...}{...}   external url
filecolor=mygreen,  %% \href{...}        local file
linkcolor=myred,    %% \href{...} and \pageref{...}
citecolor=black,
#+END_SRC

#+NAME: FmtTxtClose_head_tex_colorlinks_color
#+BEGIN_SRC d
┃",
    );
#+END_SRC

**** latex head open
***** dimensions & orientation
****** set

#+NAME: MethodOpen_head_format_string_paper_set
#+BEGIN_SRC d
string set_paper(P)(P paper_set,) {
  string paper_type_description;
#+END_SRC

#+NAME: Condition_FmtTxtOpen_head_format_string_paper_set_format_portrait
#+BEGIN_SRC d
if (paper_set.is_portrait) {
  paper_type_description = format(q"┃
#+END_SRC

#+NAME: FmtTxtSet_head_format_string_paper_set_format_portrait_tex
#+BEGIN_SRC latex
\documentclass[%s,%s,titlepage,makeidx]{scrartcl}
\usepackage{%s}
\usepackage[%s,%s]{babel}
\usepackage[autostyle, english = american]{csquotes}
%% \MakeOuterQuote{"} %% not required, using '' as quote delimiter
\selectlanguage{%s}
\hypersetup{
  pdftitle={%s},
  pdfauthor={%s},
  pdfsubject={%s},
}
#+END_SRC

#+BEGIN_SRC latex
\usepackage[%s,%s]{babel}
\usepackage[autostyle, english = american]{csquotes}
\MakeOuterQuote{"}
\setmainlanguage{}
\setotherlanguage{}
\selectlanguage{%s}
%% \usepackage{polyglossia}
#+END_SRC

#+NAME: FmtTxtSet_head_tex_set_part_section_subsection_subsubsection_paragraph_subparagraph
#+BEGIN_SRC latex
\usepackage{fancyhdr}
\lhead[ ]{ }
\chead[ \fancyplain{} \bfseries \footnotesize \leftmark ]{ \fancyplain{} \bfseries \footnotesize \rightmark }
\rhead[ ]{ }
%s
\rfoot[\tiny \href{}{}]{\textrm{\thepage}}
#+END_SRC

#+NAME: FmtTxtClose_head_format_string_paper_set_format_portrait_variables
#+BEGIN_SRC d
┃",
  paper_set.fontsize,
  paper_set.papersize,
  "./sty/" ~ paper_set.stylesheet,
  lang.codes[doc_matters.src.language]["xlp"],
  "english",
  lang.codes[doc_matters.src.language]["xlp"],
  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,
  _footer(doc_matters),
);
#+END_SRC

#+NAME: Condition_FmtTxtOpen_head_format_string_paper_set_format_landscape
#+BEGIN_SRC d
} else {
  paper_type_description = format(q"┃
#+END_SRC

#+NAME: FmtTxtSet_head_format_string_paper_set_format_landscape_tex
#+BEGIN_SRC latex
\documentclass[%s,%s,landscape,titlepage,twocolumn,makeidx]{scrartcl}
\usepackage{%s}
\usepackage[english]{babel}
%% \usepackage{polyglossia}
\setmainlanguage{%s}
\setotherlanguage{%s}
\selectlanguage{%s}
\hypersetup{
  pdftitle={%s},
  pdfauthor={%s},
  pdfsubject={%s},
}
#+END_SRC

#+NAME: FmtTxtClose_head_format_string_paper_set_format_landscape_variables
#+BEGIN_SRC d
┃",
  paper_set.fontsize,
  paper_set.papersize,
  "./sty/" ~ paper_set.stylesheet,
  lang.codes[doc_matters.src.language]["xlp"],
  "english",
  lang.codes[doc_matters.src.language]["xlp"],
  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,
  _footer(doc_matters),
);
#+END_SRC

#+NAME: MethodClose_head_format_string_paper_set_return
#+BEGIN_SRC d
  }
  return paper_type_description;
}
#+END_SRC

***** (a4, a5, b4, letter, legal) * (portrait & landscape)

#+BEGIN_SRC sh
$SpineBIN/spine --verbose --latex --set-papersize="a4,letter.portrait,b4.portrait" --output="$SpineOUT" $SpinePOD/*
#+END_SRC

#+NAME: Switch_head_format_string_paper_set_orientation
#+BEGIN_SRC d
string paper_size_orientation_latex;
switch (paper_size_orientation) {
  case "a4.portrait":      paper_size_orientation_latex = set_paper(paper.a4.portrait);      break;
  case "a4.landscape":     paper_size_orientation_latex = set_paper(paper.a4.landscape);     break;
  case "b4.portrait":      paper_size_orientation_latex = set_paper(paper.b4.portrait);      break;
  case "b4.landscape":     paper_size_orientation_latex = set_paper(paper.b4.landscape);     break;
  case "a5.portrait":      paper_size_orientation_latex = set_paper(paper.a5.portrait);      break;
  case "a5.landscape":     paper_size_orientation_latex = set_paper(paper.a5.landscape);     break;
  case "letter.portrait":  paper_size_orientation_latex = set_paper(paper.letter.portrait);  break;
  case "letter.landscape": paper_size_orientation_latex = set_paper(paper.letter.landscape); break;
  case "legal.portrait":   paper_size_orientation_latex = set_paper(paper.legal.portrait);   break;
  case "legal.landscape":  paper_size_orientation_latex = set_paper(paper.legal.landscape);  break;
  default:                 paper_size_orientation_latex = paper_type_latex.a4_portrait;
}
#+END_SRC

***** set color links

#+NAME: ConditionalSetVar_head_format_string_paper_set_color
#+BEGIN_SRC d
string links_mono_or_color_set = links.mono.strip;
if (
  (doc_matters.opt.action.latex_color_links)
  || (paper_size_orientation ==
    "a4.landscape" ||
    "a5.landscape" ||
    "b4.landscape" ||
    "letter.landscape" ||
    "legal.landscape")
){
  links_mono_or_color_set = links.mono.strip;
}
#+END_SRC

***** format latex head, open

#+NAME: FmtTxtOpen_head_format_tex_set_start_latex_head
#+BEGIN_SRC d
string _latex_head = format(q"┃%%%% spine LaTeX output%s%s
#+END_SRC

***** description comment

#+NAME: FmtTxtSet_head_tex_set_generated_by
#+BEGIN_SRC latex
%%%% %s %s
#+END_SRC

***** paper type (a4, letter, ...; ( portrait | landscape ))

- paper_type_latex.a4_portrait
- paper_type_latex.a4_landscape
- paper_type_latex.us_letter_portrait
- paper_type_latex.us_letter_landscape

#+NAME: FmtTxtSet_head_tex_set_paper_type
#+BEGIN_SRC latex
%s
#+END_SRC

***** paper margins (portrait | landscape)

- margins.portrait
- margins.landscape

#+NAME: FmtTxtSet_head_tex_set_orintation
#+BEGIN_SRC latex
%s
#+END_SRC

***** latex head

%%\usepackage{breakurl}

**** latex head close (variables set)

#+NAME: FmtTxtClose_head_a_format_string_variables
#+BEGIN_SRC d
┃",
  doc_matters.opt.action.generated_by ? " " ~ doc_matters.generator_program.name_version_and_compiler : "",
  doc_matters.opt.action.generated_by ? " (generated " ~  doc_matters.generator_program.time_output_generated ~ ")" : "",
  doc_matters.generator_program.project_name.strip,
  doc_matters.generator_program.url_home.strip,
  paper_size_orientation_latex.strip,
  margins.portrait.strip,
);
#+END_SRC

**** latex head return

#+NAME: MethodClose_head_close
#+BEGIN_SRC d
  return _latex_head.strip;
}
#+END_SRC

*** ↻ latex body :content:body:
**** latex body function

#+NAME: MethodOpen_body_function
#+BEGIN_SRC d
string latex_body(D,M)(
  const  D      doc_abstraction,
         M      doc_matters,
         string paper_size_orientation,
) {
  string _latex_body = "";
  bool _multicolumns = false;
  string _txt;
#+END_SRC

**** ↻ loop open

#+NAME: MethodLoopOpen_body_foreach_doc_part
#+BEGIN_SRC d
foreach (part; doc_matters.has.keys_seq.latex) {
  foreach (obj; doc_abstraction[part]) {
    switch (obj.metainfo.is_of_part) {
#+END_SRC

**** ↻ within loop
***** frontmatter

#+NAME: CasePart_body_for_doc_frontmatter
#+BEGIN_SRC d
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, paper_size_orientation);
      goto default;
    case "toc":
      break;
    default:
      _latex_body ~= _txt ~ "\n\n";
      _txt = "";
      break;
    }
    break;
  default: break;
  }
  break;
#+END_SRC

***** body

#+NAME: CasePart_body_for_doc_body
#+BEGIN_SRC d
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, paper_size_orientation);
      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, paper_size_orientation);
      goto default; // TODO
    default:
      _latex_body ~= _txt ~ "\n\n";
      _txt = "";
      break;
    }
    break;
  default: break;
  }
  break;
#+END_SRC

***** backmatter

#+NAME: CasePart_body_for_doc_backmatter
#+BEGIN_SRC d
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, paper_size_orientation, 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;
#+END_SRC

***** after

#+NAME: CasePart_body_for_doc_default
#+BEGIN_SRC d
    case "comment":
      break;
    default:
      { /+ debug +/
        if (doc_matters.opt.action.debug_do_latex
        && doc_matters.opt.action.vox_gt1) {
          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";
}
#+END_SRC

**** latex body return

#+NAME: MethodClose_body_function_return
#+BEGIN_SRC d
  return _latex_body;
}
#+END_SRC

*** latex tail :tail:
**** latex tail function

#+NAME: MethodOpen_tail_function
#+BEGIN_SRC d
string latex_tail(M)(
  M      doc_matters,
  string paper_size_orientation,
) {
#+END_SRC

**** latex tail starts

#+NAME: FmtTxtOpen_tail_format_string
#+BEGIN_SRC d
string _latex_tail = format(q"┃
#+END_SRC

***** latex tail format inclusions

***** latex document end

#+NAME: FmtTxtSet_tail_format_string_tex
#+BEGIN_SRC latex

\end{document}
#+END_SRC

**** latex tail format inclusions

#+NAME: FmtTxtClose_tail_format_string
#+BEGIN_SRC d
┃",
  // doc_matters.conf_make_meta.meta.title_full,
  // doc_matters.conf_make_meta.meta.creator_author,
);
#+END_SRC

**** latex tail return

#+NAME: MethodClose_tail_function_return
#+BEGIN_SRC d
  return _latex_tail;
}
#+END_SRC

* document header including copyright & license

#+NAME: doc_header_including_copyright_and_license
#+HEADER: :noweb yes
#+BEGIN_SRC emacs-lisp
<<./spine_version_info_and_doc_header_including_copyright_and_license.org:spine_doc_header_including_copyright_and_license()>>
#+END_SRC

* __END__