/+
- Name: SisuDoc Spine, Doc Reform [a part of]
  - Description: documents, structuring, processing, publishing, search
    - static content generator

  - Author: Ralph Amissah
    [ralph.amissah@gmail.com]

  - Copyright: (C) 2015 - 2025 Ralph Amissah, All Rights Reserved.

  - License: AGPL 3 or later:

    Spine (SiSU), a framework for document structuring, publishing and
    search

    Copyright (C) Ralph Amissah

    This program is free software: you can redistribute it and/or modify it
    under the terms of the GNU AFERO General Public License as published by the
    Free Software Foundation, either version 3 of the License, or (at your
    option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
    more details.

    You should have received a copy of the GNU General Public License along with
    this program. If not, see [https://www.gnu.org/licenses/].

    If you have Internet connection, the latest version of the AGPL should be
    available at these locations:
    [https://www.fsf.org/licensing/licenses/agpl.html]
    [https://www.gnu.org/licenses/agpl.html]

  - Spine (by Doc Reform, related to SiSU) uses standard:
    - docReform markup syntax
      - standard SiSU markup syntax with modified headers and minor modifications
    - docReform object numbering
      - standard SiSU object citation numbering & system

  - Homepages:
    [https://www.sisudoc.org]
    [https://www.doc-reform.org]

  - Git
    [https://git.sisudoc.org/]

+/
// document abstraction:
// abstraction of sisu markup for downstream processing
// metadoc_from_src.d
module sisudoc.meta.metadoc_from_src_functions;
@safe:
template docAbstractionFunctions() {
  // ↓ abstraction imports
  // ↓ abstraction mixins
  mixin ObjectSetter;
  mixin InternalMarkup;
  mixin spineRgxIn;
  static auto rgx = RgxI();
  // initialize
  string[string] an_object, processing, object_notes;
  string an_object_key;
  string[] anchor_tags;
  string anchor_tag;
  string anchor_tag_;
  string[string] tag_in_seg;
  string lev_anchor_tag;
  string[string][string] tag_assoc;
  string[] lv0to3_tags;
  // enum
  // biblio variables
  string biblio_tag_name, biblio_tag_entry, st;
  string[] biblio_arr_json;
  string biblio_entry_str_json;
  JSONValue[] bib_arr_json;
  int bib_entry;
  // counters
  int cntr, previous_count, previous_length;
  bool reset_note_numbers = true;
  int[string] line_occur;
  int html_segnames_ptr = 0;
  int html_segnames_ptr_cntr = 0;
  int verse_line, heading_ptr;
  // paragraph attributes
  int[string] indent;
  bool bullet = true;
  string content_non_header = "8";
  // ocn
  OCNset obj_cite_digits;
  int obj_cite_digit_, obj_cite_digit_off, obj_cite_digit_bkidx, obj_cite_digit_type;
  int[] dom_structure_markedup_tags_status         = [ 0, 0, 0, 0, 0, 0, 0, 0, 0,];
  int[] dom_structure_markedup_tags_status_buffer  = [ 0, 0, 0, 0, 0, 0, 0, 0, 0,];
  int[] dom_structure_collapsed_tags_status        = [ 0, 0, 0, 0, 0, 0, 0, 0, 0,];
  int[] dom_structure_collapsed_tags_status_buffer = [ 0, 0, 0, 0, 0, 0, 0, 0, 0,];
  static auto obj_im = ObjInlineMarkup();
  static auto obj_att = ObjAttributes();
  auto object_citation_number = OCNemitter();
  auto node_construct = NodeStructureMetadata();
  // ↓ abstraction function emitters
  // ↓ - emitters
  pure struct OCNemitter {
    int ocn_digit, ocn_object_number, ocn_on_, ocn_off_, ocn_bkidx, ocn_bkidx_;
    string object_identifier;
    bool ocn_is_off;
    auto ocn_emitter(int ocn_status_flag) {
      OCNset ocn;
      assert(ocn_status_flag <= eN.ocn.reset);
      ocn_object_number              = ocn_bkidx = 0;
      object_identifier              = "";
      ocn_is_off                     = false;
      switch(ocn_status_flag) with (eN.ocn) {
      case reset:
        ocn_digit                    = ocn_on_             = 1;
        object_identifier            = "1";
        ocn_is_off                   = false;
        ocn_off_                     = ocn_bkidx_ = 0;
        break;
      case on:
        ocn_digit                    = ocn_object_number   = ++ocn_on_;
        object_identifier            = ocn_digit.to!string;
        ocn_is_off                   = false;
        break;
      case off:
        ocn_digit                    = 0;
        ocn_off_                     = ++ocn_off_;
        object_identifier            = "a" ~ ocn_off_.to!string;
        ocn_is_off                   = true;
        break;
      case bkidx:
        ocn_bkidx                    = ++ocn_bkidx_;
        break;
      case closing: // unused?
        break;
      default:
        ocn_digit                    = 0;
      }
      assert(ocn_digit >= 0);
      ocn.digit                      = ocn_digit;
      ocn.object_number              = ocn_object_number; // difference between .object_number and .digit?
      ocn.identifier                 = object_identifier;
      ocn.off                        = ocn_is_off;
      ocn.bkidx                      = ocn_bkidx;
      ocn.type                       = ocn_status_flag;
      return ocn;
    }
    invariant() {
    }
  }
  pure ObjGenericComposite obj_heading_ancestors()(
    ObjGenericComposite  obj,
    string[]             lv_ancestors_txt,
  ) {
    switch (obj.metainfo.heading_lev_markup) {
    case 0:
      lv_ancestors_txt[0] = obj.text.to!string;
      foreach(k; 1..8) { lv_ancestors_txt[k] = ""; }
      goto default;
    case 1:
      lv_ancestors_txt[1] = obj.text.to!string;
      foreach(k; 2..8) { lv_ancestors_txt[k] = ""; }
      goto default;
    case 2:
      lv_ancestors_txt[2] = obj.text.to!string;
      foreach(k; 3..8) { lv_ancestors_txt[k] = ""; }
      goto default;
    case 3:
      lv_ancestors_txt[3] = obj.text.to!string;
      foreach(k; 4..8) { lv_ancestors_txt[k] = ""; }
      goto default;
    case 4:
      lv_ancestors_txt[4] = obj.text.to!string;
      foreach(k; 5..8) { lv_ancestors_txt[k] = ""; }
      goto default;
    case 5:
      lv_ancestors_txt[5] = obj.text.to!string;
      foreach(k; 6..8) { lv_ancestors_txt[k] = ""; }
      goto default;
    case 6:
      lv_ancestors_txt[6] = obj.text.to!string;
      lv_ancestors_txt[7] = "";
      goto default;
    case 7:
      lv_ancestors_txt[7] = obj.text.to!string;
      goto default;
    default:
      obj.tags.heading_ancestors_text = lv_ancestors_txt.dup;
    }
    return obj;
  }
  static  OCNset ocn_emit(int ocn_status_flag) {
    return object_citation_number.ocn_emitter(ocn_status_flag);
  }
  static uint[string] _check_ocn_status_()(
    char[]       line,
    uint[string] pith,
  ) {
    static auto rgx = RgxI();
    if (!(line.empty)) {
      if (pith["no_ocn_multiple_objects"] == eN.bi.off) {
        // not multi-line object, check whether object_number is on or turned off
        if (line.matchFirst(rgx.object_number_block_marks)) {                      // switch off object_number
          if (line.matchFirst(rgx.object_number_off_block)) {
            pith["no_ocn_multiple_objects"]             = eN.bi.on;
            pith["ocn"]                                 = eN.ocn.off;
            debug(ocnoff) { writeln(line); }
          }
          if (line.matchFirst(rgx.object_number_off_block_dummy_heading)) {
            pith["no_ocn_multiple_objects"]             = eN.bi.on;
            pith["dummy_heading_multiple_objects"]      = eN.bi.on;
            pith["ocn"]                                 = eN.ocn.off;
            debug(ocnoff) { writeln(line); }
          }
        } else if (pith["no_ocn_multiple_objects"] == eN.bi.off) {
            pith["dummy_heading_status"]                = eN.bi.off;
            if (pith["dummy_heading_multiple_objects"]) {
              pith["dummy_heading_status"]              = eN.bi.on;
            }
            if (line.matchFirst(rgx.object_number_off)) {
              pith["ocn"]                               = eN.ocn.off;
            } else if (line.matchFirst(rgx.object_number_off_dummy_heading)) {
              pith["ocn"]                               = eN.ocn.off;
              pith["dummy_heading_status"]              = eN.bi.on;
            } else {
              pith["ocn"]                               = eN.ocn.on;
              pith["dummy_heading_status"]              = eN.bi.off;
            }
          } else {
            pith["ocn"] = pith["no_ocn_multiple_objects"];
          }
      } else if (pith["no_ocn_multiple_objects"] == eN.bi.on) {
        if (line.matchFirst(rgx.object_number_off_block_close)) {
          pith["no_ocn_multiple_objects"]               = eN.bi.off;
          pith["ocn"]                                   = eN.ocn.on;
          pith["dummy_heading_status"]                  = eN.bi.off;
          debug(ocnoff) { writeln(line); }
        }
      }
    }
    return pith;
  }
  // ↑ - emitters ↑
  // ↓ abstraction functions
  // ↓ - reset text by line
  @system ST_txt_by_line_common_reset txt_by_line_common_reset_()(
    int[string]     line_occur,
    string[string]  an_object,
    uint[string]    pith,
  ) {
    line_occur["heading"]                               = eN.bi.off;
    line_occur["para"]                                  = eN.bi.off;
    pith["txt_is"]                                      = eN.txt_is.off;
    an_object                                           = an_object.object_reset;
    ST_txt_by_line_common_reset ret;
    {
      ret.line_occur  = line_occur;
      ret.this_object = an_object;
      ret.pith        = pith;
    }
    return ret;
  }
  // ↓ - reset object
  static string[string] object_reset()(string[string] an_object) {
    an_object.remove("body_nugget");
    an_object.remove("substantive");
    an_object.remove("is");
    an_object.remove("attrib");
    an_object.remove("bookindex_nugget");
    return an_object;
  }
  // ↑ - resets
  // ↓ - markup text by line
  char[] font_faces_line()(char[] textline) {
    static auto rgx = RgxI();
    static auto mkup = InlineMarkup();
    if (textline.match(rgx.inline_faces_line)) {
      textline = textline
        .replaceFirst(rgx.inline_emphasis_line,
          format(q"┃%s%s%s%s%s%s%s┃",
            mkup.ff_i, mkup.emph, mkup.ff_o, "$1", mkup.ff_c, mkup.emph, "$2"))
        .replaceFirst(rgx.inline_bold_line,
          format(q"┃%s%s%s%s%s%s%s┃",
            mkup.ff_i, mkup.bold, mkup.ff_o, "$1", mkup.ff_c, mkup.bold, "$2"))
        .replaceFirst(rgx.inline_underscore_line,
          format(q"┃%s%s%s%s%s%s%s┃",
            mkup.ff_i, mkup.underscore, mkup.ff_o, "$1", mkup.ff_c, mkup.underscore, "$2"))
        .replaceFirst(rgx.inline_italics_line,
          format(q"┃%s%s%s%s%s%s%s┃",
            mkup.ff_i, mkup.italic,  mkup.ff_o, "$1", mkup.ff_c, mkup.italic, "$2"));
    }
    return textline;
  }
  auto inline_markup_faces(L)(L line) {
    static auto rgx = RgxI();
    static auto mkup = InlineMarkup();
    line = replaceAll!(m => mkup.quote_o ~ m[1] ~ mkup.quote_c)(line, rgx.within_quotes);
    line = replaceAll!(m => mkup.ff_i ~ mkup.mono ~ mkup.ff_o ~ m["text"] ~ mkup.ff_c ~ mkup.mono)(line, rgx.inline_mark_mono);
    line = replaceAll!(m => mkup.ff_i ~ mkup.cite ~ mkup.ff_o ~ m["text"] ~ mkup.ff_c ~ mkup.cite)(line, rgx.inline_mark_cite);
    foreach (regx; [rgx.inline_mark_emphasis, rgx.inline_mark_bold, rgx.inline_mark_underscore, rgx.inline_mark_italics, rgx.inline_mark_superscript, rgx.inline_mark_subscript, rgx.inline_mark_strike, rgx.inline_mark_insert]) {
      line = replaceAll!(m => mkup.ff_i ~ m["mark"] ~ mkup.ff_o ~ m["text"] ~ mkup.ff_c ~ m["mark"])(line, regx);
    }
    return line;
  }
  static string links_and_images()(string obj_txt) {
    static auto rgx = RgxI();
    static auto mkup = InlineMarkup();
    if (obj_txt.match(rgx.smid_inline_url_generic)) {
      if (
        obj_txt.match(rgx.smid_inline_link_endnote_url_helper)
        || obj_txt.match(rgx.smid_inline_link_endnote_url_helper_punctuated)
      ) {
        obj_txt = replaceAll!(m => format("%s%s%s%s%s%s%s %s%s%s%s%s%s %s%s",
          mkup.lnk_o, m["content"].strip, mkup.lnk_c,
          mkup.url_o, m["link"], mkup.url_c,
          mkup.en_a_o,
          mkup.lnk_o, m["link"].strip, mkup.lnk_c,
          mkup.url_o, m["link"], mkup.url_c,
          mkup.en_a_c,
          m[3]
        ))(obj_txt, rgx.smid_inline_link_endnote_url_helper_punctuated);
        obj_txt = replaceAll!(m => format("%s%s%s%s%s%s%s %s%s%s%s%s%s %s",
          mkup.lnk_o, m["content"].strip, mkup.lnk_c,
          mkup.url_o, m["link"], mkup.url_c,
          mkup.en_a_o,
          mkup.lnk_o, m["link"].strip, mkup.lnk_c,
          mkup.url_o, m["link"], mkup.url_c,
          mkup.en_a_c
        ))(obj_txt, rgx.smid_inline_link_endnote_url_helper);
    } else {
        obj_txt = replaceAll!(m => format("%s%s%s%s%s%s%s",
          m["pre"],
          mkup.lnk_o, m["content"].strip, mkup.lnk_c,
          mkup.url_o, m["link"], mkup.url_c
        ))(obj_txt, rgx.smid_inline_link_markup_regular);
      }
        obj_txt = replaceAll!(m => format("%s%s%s%s%s%s%s",
          m["pre"],
          mkup.lnk_o, m["link"].strip, mkup.lnk_c,
          mkup.url_o, m["link"], mkup.url_c
        ))(obj_txt, rgx.smid_inline_link_naked_url); //
    }
    return obj_txt;
  }
  char[] _doc_header_and_make_substitutions_(CMM)(
    char[]  line,
    CMM     conf_make_meta,
  ) {
    enum Substitute { match, markup, }
    if (conf_make_meta.make.substitute) {
      foreach(substitution_pair; conf_make_meta.make.substitute) {
        line = line.replaceAll(
          regex("\b" ~ substitution_pair[Substitute.match]),
          substitution_pair[Substitute.markup]
        );
      }
    }
    return line;
  }
  char[] _doc_header_and_make_substitutions_fontface_(CMM)(
    char[]  line,
    CMM     conf_make_meta,
  ) {
    enum Substitute { match, markup, }
    if ( conf_make_meta.make.bold) {
      line = line.replaceAll(
        regex("\b" ~ conf_make_meta.make.bold[Substitute.match]),
        conf_make_meta.make.bold[Substitute.markup]
      );
    }
    if (conf_make_meta.make.emphasis) {
      line = line.replaceAll(
        regex("\b" ~ conf_make_meta.make.emphasis[Substitute.match]),
        conf_make_meta.make.emphasis[Substitute.markup]
      );
    }
    if (conf_make_meta.make.italics) {
      line = line.replaceAll(
        regex("\b" ~ conf_make_meta.make.italics[Substitute.match]),
        conf_make_meta.make.italics[Substitute.markup]
      );
    }
    return line;
  }
  // ↑ - markup by line
  // ↓ - text by line (blocks etc.)
  ST_txt_by_line_block_start txt_by_line_block_start()(
    char[]         line,
    uint[string]   pith,
    uint[string]   dochas,
    string[string] object_number_poem
  ) {
    static auto rgx = RgxI();
    if (auto m = line.matchFirst(rgx.block_curly_code_open)) {
      dochas["codeblock"]++;
      an_object["lang"]               = "";
      an_object["attrib"]             = (m["attrib"]) ? m["attrib"].to!string : "";
      an_object["syntax"]             = (m["syntax"]) ? m["syntax"].to!string : "";
      debug(codecurly) { writefln( "* [code curly] %s", line); }                              // code (curly) open
      pith["block_is"]                = eN.blk_is.code;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.curly;
    } else if (auto m = line.matchFirst(rgx.block_curly_poem_open)) {
      dochas["poem"]++;
      an_object["syntax"]             = "";
      an_object["attrib"]             = (m["attrib"]) ? m["attrib"].to!string : "";
      an_object["lang"]               = (m["lang"]) ? m["lang"].to!string : "";
      debug(poem) { writefln( "* [poem curly] %s", line); }                              // poem (curly) open
      object_number_poem["start"]     = obj_cite_digits.object_number.to!string;
      pith["block_is"]                = eN.blk_is.poem;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.curly;
      pith["verse_new"]               = eN.bi.on;
    } else if (auto m = line.matchFirst(rgx.block_curly_group_open)) {
      dochas["group"]++;
      an_object["syntax"]             = "";
      an_object["attrib"]             = (m["attrib"]) ? m["attrib"].to!string : "";
      an_object["lang"]               = (m["lang"]) ? m["lang"].to!string : "";
      debug(group) { writefln( "* [group curly] %s", line); }                             // group (curly) open
      pith["block_is"]                = eN.blk_is.group;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.curly;
    } else if (auto m = line.matchFirst(rgx.block_curly_block_open)) {
      dochas["block"]++;
      an_object["syntax"]             = "";
      an_object["attrib"]             = (m["attrib"]) ? m["attrib"].to!string : "";
      an_object["lang"]               = (m["lang"]) ? m["lang"].to!string : "";
      debug(block) { writefln( "* [block curly] %s", line); }
      pith["block_is"]                = eN.blk_is.block;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.curly;
    } else if (auto m = line.matchFirst(rgx.block_curly_quote_open)) {
      dochas["quote"]++;
      an_object["syntax"]             = "";
      an_object["attrib"]             = m["attrib"].to!string;
      an_object["lang"]               = m["lang"].to!string;
      debug(quote) { writefln( "* [quote curly] %s", line); }
      pith["block_is"]                = eN.blk_is.quote;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.curly;
    } else if (auto m = line.matchFirst(rgx.block_curly_table_open)) {           // curly table open
      debug(table) { writefln( "* [table curly] %s", line); }
      dochas["table"] ++;
      an_object["table_head"]         = m["attrib"].to!string;
      an_object["block_type"]         = "curly";
      pith["block_is"]                = eN.blk_is.table;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.curly;
    } else if (auto m = line.matchFirst(rgx.block_curly_table_special_markup)) { // table: special table block markup syntax!
      dochas["table"]++;
      an_object["table_head"]         = m["attrib"].to!string;
      an_object["block_type"]         = "special";
      pith["block_is"]                = eN.blk_is.table;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.curly_special;
    } else if (auto m = line.matchFirst(rgx.block_tic_code_open)) {
      dochas["codeblock"]++;
      an_object["lang"]               = "";
      an_object["attrib"]             = (m["attrib"]) ? m["attrib"].to!string : "";
      an_object["syntax"]             = (m["syntax"]) ? m["syntax"].to!string : "";
      debug(codetic) { writefln( "* [code tic] %s", line); }
      pith["block_is"]                = eN.blk_is.code;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.tic;
    } else if (auto m = line.matchFirst(rgx.block_tic_poem_open)) {
      dochas["poem"]++;
      an_object["syntax"]             = "";
      an_object["attrib"]             = (m["attrib"]) ? m["attrib"].to!string : "";
      an_object["lang"]               = (m["lang"]) ? m["lang"].to!string : "";
      debug(poem) { writefln( "* [poem tic] %s", line); }
      object_number_poem["start"]     = obj_cite_digits.object_number.to!string;
      pith["block_is"]                = eN.blk_is.poem;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.tic;
      pith["verse_new"]               = eN.bi.on;
    } else if (auto m = line.matchFirst(rgx.block_tic_group_open)) {
      dochas["group"]++;
      an_object["syntax"]             = "";
      an_object["attrib"]             = (m["attrib"]) ? m["attrib"].to!string : "";
      an_object["lang"]               = (m["lang"]) ? m["lang"].to!string : "";
      debug(group) { writefln( "* [group tic] %s", line); }
      pith["block_is"]                = eN.blk_is.group;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.tic;
    } else if (auto m = line.matchFirst(rgx.block_tic_block_open)) {
      dochas["block"]++;
      an_object["syntax"]             = "";
      an_object["attrib"]             = (m["attrib"]) ? m["attrib"].to!string : "";
      an_object["lang"]               = (m["lang"]) ? m["lang"].to!string : "";
      debug(block) { writefln( "* [block tic] %s", line); }
      pith["block_is"]                = eN.blk_is.block;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.tic;
    } else if (auto m = line.matchFirst(rgx.block_tic_quote_open)) {
      dochas["quote"]++;
      an_object["syntax"]             = "";
      an_object["attrib"]             = m["attrib"].to!string;
      an_object["lang"]               = m["lang"].to!string;
      debug(quote) { writefln( "* [quote tic] %s", line);                        // quote (tic) open
      }
      pith["block_is"]                = eN.blk_is.quote;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.tic;
    } else if (auto m = line.matchFirst(rgx.block_tic_table_open)) {             // tic table open
      debug(table) { writefln( "* [table tic] %s", line); }
      dochas["table"] ++;
      an_object["table_head"]         = m["attrib"].to!string;
      an_object["block_type"]         = "tic";
      pith["block_is"]                = eN.blk_is.table;
      pith["block_state"]             = eN.blk_state.on;
      pith["block_delim"]             = eN.blk_delim.tic;
    }
    ST_txt_by_line_block_start ret;
    {
      ret.pith               = pith;
      ret.dochas             = dochas;
      ret.object_number_poem = object_number_poem;
    }
    return ret;
  }
  ST_txt_by_line_block_generic txt_by_line_block_group()(
    char[]          line,
    string[string]  an_object,
    uint[string]    pith,
  ) {
    static auto rgx = RgxI();
    if (pith["block_is"] == eN.blk_is.group) {
      if (pith["block_delim"] == eN.blk_delim.curly) {
        if (line.matchFirst(rgx.block_curly_group_close)) {
          debug(group) { writeln(line); }
          an_object[an_object_key]    = an_object[an_object_key].stripRight;
          pith["block_is"]            = eN.blk_is.group;
          pith["block_state"]         = eN.blk_state.closing;
          pith["block_delim"]         = eN.blk_delim.off;
        } else {
          debug(group) { writeln(line); }
          an_object[an_object_key] ~= line ~= "\n";
        }
      } else if (pith["block_delim"] == eN.blk_delim.tic) {
        if (line.matchFirst(rgx.block_tic_close)) {
          debug(group) { writeln(line); }
          an_object[an_object_key]    = an_object[an_object_key].stripRight;
          pith["block_is"]            = eN.blk_is.group;
          pith["block_state"]         = eN.blk_state.closing;
          pith["block_delim"]         = eN.blk_delim.off;
        } else {
          debug(group) { writeln(line); }
          an_object[an_object_key] ~= line ~= "\n";
        }
      }
    }
    ST_txt_by_line_block_generic ret;
    {
      ret.pith        = pith;
      ret.this_object = an_object;
    }
    return ret;
  }
  ST_txt_by_line_block_generic txt_by_line_block_block()(
    char[]          line,
    string[string]  an_object,
    uint[string]    pith,
  ) {
    static auto rgx = RgxI();
    if (pith["block_is"] == eN.blk_is.block) {
      if (pith["block_delim"] == eN.blk_delim.curly) {
        if (line.matchFirst(rgx.block_curly_block_close)) {
          debug(block) { writeln(line); }
          an_object[an_object_key]    = an_object[an_object_key].stripRight;
          pith["block_is"]            = eN.blk_is.block;
          pith["block_state"]         = eN.blk_state.closing;
          pith["block_delim"]         = eN.blk_delim.off;
        } else {
          debug(block) { writeln(line); }
          an_object[an_object_key] ~= line ~= "\n";
        }
      } else if (pith["block_delim"] == eN.blk_delim.tic) {
        if (line.matchFirst(rgx.block_tic_close)) {
          debug(block) { writeln(line); }
          an_object[an_object_key]    = an_object[an_object_key].stripRight;
          pith["block_is"]            = eN.blk_is.block;
          pith["block_state"]         = eN.blk_state.closing;
          pith["block_delim"]         = eN.blk_delim.off;
        } else {
          debug(block) { writeln(line); }
          an_object[an_object_key] ~= line ~= "\n";
        }
      }
    }
    ST_txt_by_line_block_generic ret;
    {
      ret.pith        = pith;
      ret.this_object = an_object;
    }
    return ret;
  }
  ST_txt_by_line_block_poem txt_by_line_block_poem(CMM)(
    char[]          line,
    string[string]  an_object,
    uint[string]    pith,
    int             cntr,
    string[string]  object_number_poem,
    CMM             conf_make_meta,
    string[string]  tag_in_seg,
  ) {
    static auto rgx = RgxI();
    if (pith["block_is"] == eN.blk_is.poem) {
      if (pith["block_delim"] == eN.blk_delim.curly) {
        if (line.matchFirst(rgx.block_curly_poem_close)) {
          if (an_object_key in an_object
          || processing.length > 0) {
            an_object[an_object_key]        = "";
            debug(poem) { writefln( "* [poem curly] %s", line); }
            if (processing.length > 0) {
              an_object[an_object_key]      = processing["verse"];
            }
            debug(poem) {
              writeln(__LINE__);
              writefln( "* %s %s", obj_cite_digits.object_number, line);
            }
            if (an_object.length > 0) {
              debug(poem) { writeln( obj_cite_digits.object_number, an_object[an_object_key]); }
              an_object["is"]                                   = "verse";
              ST_txtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_struct
                = obj_im.obj_inline_markup_and_anchor_tags_and_misc(an_object, an_object_key, conf_make_meta, No._new_doc);
              an_object["substantive"]                          = substantive_obj_misc_struct.obj_txt;
              anchor_tag                                        = substantive_obj_misc_struct.anchor_tag;
              comp_obj_                                         = set_object_generic("body", "body", "block", "verse", an_object["substantive"], obj_cite_digits.object_number);
              comp_obj_.metainfo.identifier                     = obj_cite_digits.identifier;
              comp_obj_.metainfo.object_number_off              = obj_cite_digits.off;
              comp_obj_.metainfo.o_n_book_index                 = obj_cite_digits.bkidx;
              comp_obj_.metainfo.object_number_type             = obj_cite_digits.type;
              comp_obj_.tags.html_segment_anchor_tag_is         = tag_in_seg["seg_lv4"];
              comp_obj_.tags.epub_segment_anchor_tag_is         = tag_in_seg["seg_lv1to4"];
              comp_obj_.has.inline_notes_reg                    = substantive_obj_misc_struct.has_notes_reg;
              comp_obj_.has.inline_notes_star                   = substantive_obj_misc_struct.has_notes_star;
              comp_obj_.has.inline_links                        = substantive_obj_misc_struct.has_links;
              the_document_body_section                         ~= comp_obj_;
              tag_assoc                                         = an_object.inline_para_link_anchor(tag_in_seg, tag_assoc);
            }
            object_reset(an_object);
            processing.remove("verse");
            ++cntr;
          }
          object_number_poem["end"]   = obj_cite_digits.object_number.to!string;
          pith["block_is"]            = eN.blk_is.poem;
          pith["block_state"]         = eN.blk_state.closing;
          pith["block_delim"]         = eN.blk_delim.off;
        } else {
          processing["verse"] ~= line ~= "\n";
          if (pith["verse_new"] == eN.bi.on) {
            obj_cite_digits = ocn_emit(pith["ocn"]);
            pith["verse_new"]         = eN.bi.off;
          } else if (line.matchFirst(rgx.newline_eol_delimiter_only)) {
            processing["verse"]       = processing["verse"].stripRight;
            verse_line                = eN.bi.off;
            pith["verse_new"]         = eN.bi.on;
          }
          if (pith["verse_new"] == eN.bi.on) {
            verse_line = 1;
            an_object[an_object_key]  = processing["verse"];
            debug(poem) { writefln(
                "* %s curly\n%s",
                obj_cite_digits.object_number,
                an_object[an_object_key]
              );
            }
            processing.remove("verse");
            an_object["is"]                                     = "verse";
            auto comp_obj_location = node_construct.node_location_emitter(
              content_non_header,
              tag_in_seg,
              lev_anchor_tag,
              tag_assoc,
              obj_cite_digits,
              cntr,
              heading_ptr-1,
              an_object["is"]
            );
            ST_txtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_struct
              = obj_im.obj_inline_markup_and_anchor_tags_and_misc(an_object, an_object_key, conf_make_meta, No._new_doc);
            an_object["substantive"]                            = substantive_obj_misc_struct.obj_txt;
            anchor_tag                                          = substantive_obj_misc_struct.anchor_tag;
            comp_obj_                                           = set_object_generic("body", "body", "block", "verse", an_object["substantive"], obj_cite_digits.object_number);
            comp_obj_.metainfo.identifier                       = obj_cite_digits.identifier;
            comp_obj_.metainfo.object_number_off                = obj_cite_digits.off;
            comp_obj_.metainfo.o_n_book_index                   = obj_cite_digits.bkidx;
            comp_obj_.metainfo.object_number_type               = obj_cite_digits.type;
            comp_obj_.tags.html_segment_anchor_tag_is           = tag_in_seg["seg_lv4"];
            comp_obj_.tags.epub_segment_anchor_tag_is           = tag_in_seg["seg_lv1to4"];
            comp_obj_.has.inline_notes_reg                      = substantive_obj_misc_struct.has_notes_reg;
            comp_obj_.has.inline_notes_star                     = substantive_obj_misc_struct.has_notes_star;
            comp_obj_.has.inline_links                          = substantive_obj_misc_struct.has_links;
            the_document_body_section                           ~= comp_obj_;
            tag_assoc                                           = an_object.inline_para_link_anchor(tag_in_seg, tag_assoc);
            object_reset(an_object);
            processing.remove("verse");
            ++cntr;
          }
        }
      } else if (pith["block_delim"] == eN.blk_delim.tic) {
        if (auto m = line.matchFirst(rgx.block_tic_close)) {
          an_object[an_object_key] = "verse";
          debug(poem) { writefln( "* [poem tic] %s", line); }
          if (processing.length > 0) {
            an_object[an_object_key]  = processing["verse"];
          }
          if (an_object.length > 0) {
            debug(poem) { writeln(__LINE__); writeln(obj_cite_digits.object_number, line); }
            processing.remove("verse");
            an_object["is"]                                     = "verse";
            ST_txtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_struct
              = obj_im.obj_inline_markup_and_anchor_tags_and_misc(an_object, an_object_key, conf_make_meta, No._new_doc);
            an_object["substantive"]                            = substantive_obj_misc_struct.obj_txt;
            anchor_tag                                          = substantive_obj_misc_struct.anchor_tag;
            comp_obj_                                           = set_object_generic("body", "body", "block", "verse", an_object["substantive"], obj_cite_digits.object_number);
            comp_obj_.metainfo.identifier                       = obj_cite_digits.identifier;
            comp_obj_.metainfo.object_number_off                = obj_cite_digits.off;
            comp_obj_.metainfo.o_n_book_index                   = obj_cite_digits.bkidx;
            comp_obj_.metainfo.object_number_type               = obj_cite_digits.type;
            comp_obj_.tags.html_segment_anchor_tag_is           = tag_in_seg["seg_lv4"];
            comp_obj_.tags.epub_segment_anchor_tag_is           = tag_in_seg["seg_lv1to4"];
            comp_obj_.has.inline_notes_reg                      = substantive_obj_misc_struct.has_notes_reg;
            comp_obj_.has.inline_notes_star                     = substantive_obj_misc_struct.has_notes_star;
            comp_obj_.has.inline_links                          = substantive_obj_misc_struct.has_links;
            the_document_body_section                           ~= comp_obj_;
            tag_assoc                                           = an_object.inline_para_link_anchor(tag_in_seg, tag_assoc);
            object_number_poem["end"]                           = obj_cite_digits.object_number.to!string;
            object_reset(an_object);
            processing.remove("verse");
            ++cntr;
          }
          pith["block_is"]            = eN.blk_is.poem;
          pith["block_state"]         = eN.blk_state.closing;
          pith["block_delim"]         = eN.blk_delim.off;
        } else {
          processing["verse"]         ~= line ~= "\n";
          if (pith["verse_new"] == eN.bi.on) {
            obj_cite_digits           = ocn_emit(pith["ocn"]);
            pith["verse_new"]         = eN.bi.off;
          } else if (line.matchFirst(rgx.newline_eol_delimiter_only)) {
            processing["verse"]       = processing["verse"].stripRight;
            pith["verse_new"]         = eN.bi.on;
            verse_line                = eN.bi.off;
          }
          if (pith["verse_new"] == eN.bi.on) {
            verse_line = 1;
            an_object[an_object_key]  = processing["verse"];
            debug(poem) { writefln(
                "* %s tic\n%s",
                obj_cite_digits.object_number,
                an_object[an_object_key]
              );
            }
            processing.remove("verse");
            an_object["is"]                                     = "verse";
            auto comp_obj_location
              = node_construct.node_location_emitter(
                content_non_header,
                tag_in_seg,
                lev_anchor_tag,
                tag_assoc,
                obj_cite_digits,
                cntr,
                heading_ptr-1,
                an_object["is"]
              );
            ST_txtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_struct
              = obj_im.obj_inline_markup_and_anchor_tags_and_misc(an_object, an_object_key, conf_make_meta, No._new_doc);
            an_object["substantive"]                            = substantive_obj_misc_struct.obj_txt;
            anchor_tag                                          = substantive_obj_misc_struct.anchor_tag;
            comp_obj_                                           = set_object_generic("body", "body", "block", "verse", an_object["substantive"], obj_cite_digits.object_number);
            comp_obj_.metainfo.identifier                       = obj_cite_digits.identifier;
            comp_obj_.metainfo.object_number_off                = obj_cite_digits.off;
            comp_obj_.metainfo.o_n_book_index                   = obj_cite_digits.bkidx;
            comp_obj_.metainfo.object_number_type               = obj_cite_digits.type;
            comp_obj_.tags.html_segment_anchor_tag_is           = tag_in_seg["seg_lv4"];
            comp_obj_.tags.epub_segment_anchor_tag_is           = tag_in_seg["seg_lv1to4"];
            comp_obj_.has.inline_notes_reg                      = substantive_obj_misc_struct.has_notes_reg;
            comp_obj_.has.inline_notes_star                     = substantive_obj_misc_struct.has_notes_star;
            comp_obj_.has.inline_links                          = substantive_obj_misc_struct.has_links;
            the_document_body_section                           ~= comp_obj_;
            tag_assoc                                           = an_object.inline_para_link_anchor(tag_in_seg, tag_assoc);
            object_reset(an_object);
            processing.remove("verse");
            ++cntr;
          }
        }
      }
    }
    ST_txt_by_line_block_poem ret;
    {
      ret.cntr        = cntr;
      ret.pith        = pith;
      ret.this_object = an_object;
    }
    return ret;
  }
  ST_txt_by_line_block_generic txt_by_line_block_code()(
    char[]          line,
    string[string]  an_object,
    uint[string]    pith,
  ) {
    static auto rgx = RgxI();
    if ( pith["block_is"] == eN.blk_is.code) {
      if (pith["block_delim"] == eN.blk_delim.curly) {
        if (line.matchFirst(rgx.block_curly_code_close)) {
          debug(codecurly) { writeln(line); }
          an_object[an_object_key] = an_object[an_object_key]
            .replaceFirst(rgx.newline_eol_delimiter_only, "")
            .stripRight;
          pith["block_is"]            = eN.blk_is.code;
          pith["block_state"]         = eN.blk_state.closing;
          pith["block_delim"]         = eN.blk_delim.off;
        } else {
          debug(codecurly) { writeln(line); }
          an_object[an_object_key] ~= line ~= "\n";
        }
      } else if (pith["block_delim"] == eN.blk_delim.tic) {
        if (line.matchFirst(rgx.block_tic_close)) {
          debug(codetic) { writeln(line); }
          an_object[an_object_key] = an_object[an_object_key]
            .replaceFirst(rgx.newline_eol_delimiter_only, "")
            .stripRight;
          pith["block_is"]            = eN.blk_is.code;
          pith["block_state"]         = eN.blk_state.closing;
          pith["block_delim"]         = eN.blk_delim.off;
        } else {
          debug(codetic) { writeln(line); }
          an_object[an_object_key] ~= line ~= "\n";
        }
      }
    }
    ST_txt_by_line_block_generic ret;
    {
      ret.pith        = pith;
      ret.this_object = an_object;
    }
    return ret;
  }
  @system auto txt_by_line_block_table(CMM)(
    char[]          line,
    string[string]  an_object,
    uint[string]    pith,
    CMM             conf_make_meta,
  ) {
    static auto rgx = RgxI();
    if (pith["block_is"] == eN.blk_is.table) {
      if (pith["block_delim"] == eN.blk_delim.curly) {
        if (line.matchFirst(rgx.block_curly_table_close)) {
          debug(table) { writeln(line); }
          pith["block_is"]            = eN.blk_is.table;
          pith["block_state"]         = eN.blk_state.closing;
          pith["block_delim"]         = eN.blk_delim.off;
        } else {
          debug(table) { writeln(line); }
          an_object[an_object_key] ~= line ~= "\n";
        }
      } else if (pith["block_delim"] == eN.blk_delim.curly_special) {
        if (line.empty) {
          pith["block_is"]            = eN.blk_is.table;
          pith["block_state"]         = eN.blk_state.off;
          pith["block_delim"]         = eN.blk_delim.off;
          {
            auto _get = line.flow_table_closed_make_special_notation_table_(
              an_object,
              the_document_body_section,
              obj_cite_digits,
              comp_obj_,
              cntr,
              pith,
              conf_make_meta,
            );
            {
              an_object                 = _get.this_object;
              the_document_body_section = _get.the_document_body_section;
              obj_cite_digits           = _get.obj_cite_digits;
              comp_obj_                 = _get.comp_obj_;
              cntr                      = _get.cntr;
              pith                      = _get.pith;
            }
          }
        } else {
          debug(table) { writeln(line); }
          an_object[an_object_key] ~= line ~= "\n";
        }
      } else if (pith["block_delim"] == eN.blk_delim.tic) {
        if (line.matchFirst(rgx.block_tic_close)) {
          debug(table) { writeln(line); }
          pith["block_is"]            = eN.blk_is.table;
          pith["block_state"]         = eN.blk_state.closing;
          pith["block_delim"]         = eN.blk_delim.off;
        } else {
          debug(table) { writeln(line); }
          an_object[an_object_key] ~= line ~= "\n";
        }
      }
    }
    struct ST_txt_by_line_block_table {
      CMM             conf_make_meta;
      uint[string]    pith;
      string[string]  this_object;
    }
    ST_txt_by_line_block_table ret;
    {
      ret.conf_make_meta = conf_make_meta,
      ret.pith           = pith;
      ret.this_object    = an_object;
    }
    return ret;
  }
  ST_txt_by_line_block_generic txt_by_line_block_quote()(
    char[]          line,
    string[string]  an_object,
    uint[string]    pith,
  ) {
    static auto rgx = RgxI();
    if (pith["block_is"] == eN.blk_is.quote){
      if (pith["block_delim"] == eN.blk_delim.curly) {
        if (line.matchFirst(rgx.block_curly_quote_close)) {
          debug(quote) { writeln(line); }
          an_object[an_object_key]    = an_object[an_object_key].stripRight;
          pith["block_is"]            = eN.blk_is.quote;
          pith["block_state"]         = eN.blk_state.closing;
          pith["block_delim"]         = eN.blk_delim.off;
        } else {
          debug(quote) { writeln(line); }
          an_object[an_object_key] ~= line ~= "\n";
        }
      } else if (pith["block_delim"] == eN.blk_delim.tic) {
        if (line.matchFirst(rgx.block_tic_close)) {
          debug(quote) { writeln(line); }
          an_object[an_object_key]    = an_object[an_object_key].stripRight;
          pith["block_is"]            = eN.blk_is.quote;
          pith["block_state"]         = eN.blk_state.closing;
          pith["block_delim"]         = eN.blk_delim.off;
        } else {
          debug(quote) { writeln(line); }
          an_object[an_object_key] ~= line ~= "\n";
        }
      }
    }
    ST_txt_by_line_block_generic ret;
    {
      ret.pith        = pith;
      ret.this_object = an_object;
    }
    return ret;
  }
  @system ST_txt_by_line_block_biblio txt_by_line_block_biblio(
    char[]                  line,
    uint[string] pith,
    int          bib_entry,
    string       biblio_entry_str_json,
    string[]     biblio_arr_json,
  ) {
    mixin spineBiblio;
    auto jsn = BibJsnStr();
    static auto rgx = RgxI();
    string biblio_tag_map()(string abr) {
      auto btm = [
        "au"        : "author_raw",
        "ed"        : "editor_raw",
        "ti"        : "fulltitle",
        "lng"       : "language",
        "jo"        : "journal",
        "vol"       : "volume",
        "edn"       : "edition",
        "yr"        : "year",
        "pl"        : "place",
        "pb"        : "publisher",
        "pub"       : "publisher",
        "pg"        : "pages",
        "pgs"       : "pages",
        "sn"        : "short_name"
      ];
      return btm[abr];
    }
    if (line.matchFirst(rgx.heading_biblio)) {
      pith["section"] = eN.sect.bibliography;
    }
    if (line.empty) {
      debug {
        debug(biblioblock) { writeln("---"); }
        debug(biblioblockinclude) { writeln(biblio_entry_str_json.length); }
      }
      if ((bib_entry == eN.bi.off)
      && (biblio_entry_str_json.empty)) {
        bib_entry = eN.bi.on;
        biblio_entry_str_json = jsn.biblio_entry_tags_jsonstr;
      } else if (!(biblio_entry_str_json.empty)) {
        bib_entry = eN.bi.off;
        if (!(biblio_entry_str_json == jsn.biblio_entry_tags_jsonstr)) {
          auto biblio_entry = parseJSON(biblio_entry_str_json);
          if (biblio_entry["fulltitle"].str.empty) {
            writeln("check problem entry (Title missing): ", biblio_entry_str_json);
          } else if ((biblio_entry["author_raw"].str.empty) && (biblio_entry["editor_raw"].str.empty)) {
            writeln("check problem entry (No author and no editor): ", biblio_entry_str_json);
          } else {
            biblio_arr_json ~= biblio_entry_str_json;
          }
          biblio_entry_str_json = jsn.biblio_entry_tags_jsonstr;
        }
      } else {
        writeln("?? 2. ERROR ", biblio_entry_str_json, "??");
        biblio_entry_str_json = "";
      }
    } else if (line.matchFirst(rgx.biblio_tags)) {
      debug(biblioblock) { writeln(line); }
      auto bt = line.match(rgx.biblio_tags);
      bib_entry = eN.bi.off;
      st = bt.captures[1].to!string;
      auto header_tag_value = (bt.captures[2]).to!string;
      JSONValue j = parseJSON(biblio_entry_str_json);
      biblio_tag_name = (st.match(rgx.biblio_abbreviations))
        ? (biblio_tag_map(st))
        : st;
      j.object[biblio_tag_name] = header_tag_value;
      debug(bibliounsortedcheckduplicates) { writeln(biblio_tag_name, ": ", header_tag_value); writeln("--"); }
      switch (biblio_tag_name) {
      case "author_raw": // author_arr author (fn sn)
        j["author_arr"]
         = header_tag_value.split(rgx.arr_delimiter);
        string tmp;
        biblioAuthorLoop:
        foreach (au; j["author_arr"].array) {
          if (auto x = au.str.match(rgx.name_delimiter)) {
            tmp ~= x.captures[2] ~ " " ~ x.captures[1] ~ ", ";
          } else {
            tmp ~= au.str;
          }
        }
        tmp = tmp.replace(rgx.trailing_comma, "");
        j["author"].str = tmp;
        goto default;
      case "editor_raw": // editor_arr editor (fn sn)
        j["editor_arr"]
          = header_tag_value.split(rgx.arr_delimiter);
        string tmp;
        biblioEditorLoop:
        foreach (ed; j["editor_arr"].array) {
          if (auto x = ed.str.match(rgx.name_delimiter)) {
            tmp ~= x.captures[2] ~ " " ~ x.captures[1] ~ ", ";
          } else {
            tmp ~= ed.str;
          }
        }
        tmp = tmp.replace(rgx.trailing_comma, "");
        j["editor"].str = tmp;
        goto default;
      case "fulltitle": // title & subtitle
        goto default;
      default:
        break;
      }
      auto s = j.toString();
      debug(biblio1) { writefln(
          "* %s: %s\n%s",
          biblio_tag_name,
          biblio_tag_entry,
          j[biblio_tag_name]
        );
      }
      if (line.match(rgx.comment)) {
        writeln("ERROR", line, "COMMENT");
        writeln("ERROR", s, "%%");
      }
      if (!(match(line, rgx.comment))) {
        debug(biblioblockinclude) { writeln(line); }
        biblio_entry_str_json = s;
      } else {
        biblio_entry_str_json = "";
      }
      header_tag_value        = "";
    }
    ST_txt_by_line_block_biblio ret;
    {
      ret.pith                  = pith;
      ret.bib_entry             = bib_entry;
      ret.biblio_entry_str_json = biblio_entry_str_json;
      ret.biblio_arr_json       = biblio_arr_json;
    }
    return ret;
  }
  // ↑ - text by line
  // ↓ - para
  string[string][string] inline_para_link_anchor()(
    string[string]          an_object,
    string[string]          tag_in_seg,
    string[string][string]  tag_assoc
  ) {
    static auto rgx = RgxI();
    if (auto m = an_object["substantive"].match(rgx.inline_link_anchor)) {
      if (m.captures[1] !in tag_assoc) {
        tag_assoc[(m.captures[1])]["seg_lv4"]    = tag_in_seg["seg_lv4"];
        tag_assoc[(m.captures[1])]["seg_lv1to4"] = tag_in_seg["seg_lv1to4"];
      } else {
        writeln("a tag named  already exists, check text line\n    ", an_object["substantive"]);
      }
    }
    return tag_assoc;
  }
  ST_flow_para_match flow_para_match_()(
    char[]         line,
    string[string]  an_object,
    string          an_object_key,
    int[string]     indent,
    bool            bullet,
    uint[string]    pith,
    int[string]     line_occur,
  ) {
    static auto rgx = RgxI();
    if (line_occur["para"] == eN.bi.off) {
      line = font_faces_line(line);
      // para matches
      pith["txt_is"]           = eN.txt_is.para;
      an_object[an_object_key] ~= line;
      indent = [
        "hang_position" : 0,
        "base_position" : 0,
      ];
      bullet = false;
      if (auto m = line.matchFirst(rgx.para_indent)) {
        debug(paraindent) { writeln(line); }
        indent["hang_position"] = (m["indent"]).to!int;
        indent["base_position"] = (m["indent"]).to!int;
      } else if (line.matchFirst(rgx.para_bullet)) {
        debug(parabullet) { writeln(line); }
        bullet = true;
      } else if (auto m = line.matchFirst(rgx.para_indent_hang)) {
        debug(paraindenthang) { writeln(line); }
        indent = [
          "hang_position" : (m["hang"]).to!int,
          "base_position" : (m["indent"]).to!int,
        ];
      } else if (auto m = line.matchFirst(rgx.para_bullet_indent)) {
        debug(parabulletindent) { writeln(line); }
        indent = [
          "hang_position" : (m["indent"]).to!int,
          "base_position" : (m["indent"]).to!int,
        ];
        bullet = true;
      }
      ++line_occur["para"];
    }
    ST_flow_para_match ret;
    {
      ret.pith            = pith;
      ret.this_object     = an_object;
      ret.this_object_key = an_object_key;
      ret.indent          = indent;
      ret.bullet          = bullet;
      ret.line_occur      = line_occur;
    }
    return ret;
  }
  // ↑ - para
  // ↓ - heading
  ST_flow_heading_found flow_heading_found_()(
    char[]                line,
    string[string]        heading_match_str,
    string[]              _make_unmarked_headings,
    Regex!(char)[string]  heading_match_rgx,
    uint[string]          pith,
  ) {
    static auto rgx = RgxI();
    if ((_make_unmarked_headings.length > 2)
    && (pith["make_headings"] == eN.bi.off)) {                        // headings found
      debug(headingsfound) { writeln(_make_unmarked_headings); }
      debug(headingsfound) {
        writeln(_make_unmarked_headings.length);
        writeln(_make_unmarked_headings);
      }
      switch (_make_unmarked_headings.length) {
      case 7 :
        if (!empty(_make_unmarked_headings[6])) {
          heading_match_str["h_4"]
            = "^(" ~ _make_unmarked_headings[6].to!string ~ ")";
          heading_match_rgx["h_4"]
            = regex(heading_match_str["h_4"]);
        }
        goto case;
      case 6 :
        if (!empty(_make_unmarked_headings[5])) {
          heading_match_str["h_3"]
            = "^(" ~ _make_unmarked_headings[5].to!string ~ ")";
          heading_match_rgx["h_3"]
            = regex(heading_match_str["h_3"]);
        }
        goto case;
      case 5 :
        if (!empty(_make_unmarked_headings[4])) {
          heading_match_str["h_2"]
            = "^(" ~ _make_unmarked_headings[4].to!string ~ ")";
          heading_match_rgx["h_2"]
            = regex(heading_match_str["h_2"]);
        }
        goto case;
      case 4 :
        if (!empty(_make_unmarked_headings[3])) {
          heading_match_str["h_1"]
            = "^(" ~ _make_unmarked_headings[3].to!string ~ ")";
          heading_match_rgx["h_1"]
            = regex(heading_match_str["h_1"]);
        }
        goto case;
      case 3 :
        if (!empty(_make_unmarked_headings[2])) {
          heading_match_str["h_D"]
            = "^(" ~ _make_unmarked_headings[2].to!string ~ ")";
          heading_match_rgx["h_D"]
            = regex(heading_match_str["h_D"]);
        }
        goto case;
      case 2 :
        if (!empty(_make_unmarked_headings[1])) {
          heading_match_str["h_C"]
            = "^(" ~ _make_unmarked_headings[1].to!string ~ ")";
          heading_match_rgx["h_C"]
            = regex(heading_match_str["h_C"]);
        }
        goto case;
      case 1 :
        if (!empty(_make_unmarked_headings[0])) {
          heading_match_str["h_B"]
            = "^(" ~ _make_unmarked_headings[0].to!string ~ ")";
          heading_match_rgx["h_B"]
            = regex(heading_match_str["h_B"]);
        }
        break;
      default:
        break;
      }
      pith["make_headings"] = eN.bi.on;
    }
    ST_flow_heading_found ret;
    {
      ret.heading_match_str = heading_match_str;
      ret.heading_match_rgx = heading_match_rgx;
      ret.pith              = pith;
    }
    return ret;
  }
  ST_flow_heading_make_set flow_heading_make_set_()(
               char[]                line,
               int[string]           line_occur,
    return ref Regex!(char)[string]  heading_match_rgx,
    return ref uint[string]          pith,
  ) {
    if (pith["make_headings"] == eN.bi.on
      && (line_occur["para"] == eN.bi.off
      && line_occur["heading"] == eN.bi.off)
      && pith["txt_is"] == eN.txt_is.off
    ) {                             // heading make set
      if (line.matchFirst(heading_match_rgx["h_B"])) {
        line = "B~ " ~ line;
        debug(headingsfound) { writeln(line); }
      }
      if (line.matchFirst(heading_match_rgx["h_C"])) {
        line = "C~ " ~ line;
        debug(headingsfound) { writeln(line); }
      }
      if (line.matchFirst(heading_match_rgx["h_D"])) {
        line = "D~ " ~ line;
        debug(headingsfound) { writeln(line); }
      }
      if (line.matchFirst(heading_match_rgx["h_1"])) {
        line = "1~ " ~ line;
        debug(headingsfound) { writeln(line); }
      }
      if (line.matchFirst(heading_match_rgx["h_2"])) {
        line = "2~ " ~ line;
        debug(headingsfound) { writeln(line); }
      }
      if (line.matchFirst(heading_match_rgx["h_3"])) {
        line = "3~ " ~ line;
        debug(headingsfound) { writeln(line); }
      }
      if (line.matchFirst(heading_match_rgx["h_4"])) {
        line = "4~ " ~ line;
        debug(headingsfound) { writeln(line); }
      }
    }
    ST_flow_heading_make_set ret;
    {
      ret.line           = line;
      ret.pith           = pith;
      ret.this_object    = an_object;
    }
    return ret;
  }
  auto flow_heading_matched_(CMM)(
    char[]          line,
    string[string]  an_object,
    int[string]     line_occur,
    string          an_object_key,
    int[string]     lv,
    int[string]     collapsed_lev,
    uint[string]    pith,
    CMM             conf_make_meta,
  ) {
    static auto rgx = RgxI();
    static auto mkup = InlineMarkup();
    if (auto m = line.match(rgx.headings)) {                                      // heading match
      ++line_occur["heading"];
      pith["txt_is"]           = eN.txt_is.heading;
      if (line.match(rgx.heading_seg_and_above)) {
        pith["section"]        = eN.sect.unset;
      }
      an_object[an_object_key] ~= line ~= "\n";
      an_object["lev"] ~= m.captures[1];
      assertions_doc_structure(an_object, an_object_key, lv); // includes most of the logic for collapsed levels
      switch (an_object["lev"]) {
      case "A":                                // Title set
        if ((an_object[an_object_key].match(rgx.variable_doc_title_author_date))
        || (an_object[an_object_key].match(rgx.variable_doc_title)
        && an_object[an_object_key].match(rgx.variable_doc_author)
        && an_object[an_object_key].match(rgx.variable_doc_date))) {
          an_object[an_object_key] = an_object[an_object_key]
            .replaceFirst(rgx.variable_doc_title_author_date,
              (conf_make_meta.meta.title_full
              ~ mkup.br_line_inline
              ~ conf_make_meta.meta.creator_author
              ~ " (" ~ (conf_make_meta.meta.date_published.replaceFirst(regex(r"(?:-00)+"),"")) ~ ")"))
            .replaceFirst(rgx.variable_doc_title,
              (conf_make_meta.meta.title_full ~ mkup.br_line_inline))
            .replaceFirst(rgx.variable_doc_author,
              conf_make_meta.meta.creator_author)
            .replaceFirst(rgx.variable_doc_date,
              " (" ~ (conf_make_meta.meta.date_published.replaceFirst(regex(r"(?:-00)+"),"")) ~ ")");
        } else if ((an_object[an_object_key].match(rgx.variable_doc_title_author))
        || (an_object[an_object_key].match(rgx.variable_doc_title)
        && an_object[an_object_key].match(rgx.variable_doc_author))) {
          an_object[an_object_key] = an_object[an_object_key]
            .replaceFirst(rgx.variable_doc_title_author_date,
              (conf_make_meta.meta.title_full
              ~ mkup.br_line_inline
              ~ conf_make_meta.meta.creator_author))
            .replaceFirst(rgx.variable_doc_title,
              (conf_make_meta.meta.title_full ~ mkup.br_line_inline))
            .replaceFirst(rgx.variable_doc_author,
              conf_make_meta.meta.creator_author);
        } else if (an_object[an_object_key].match(rgx.variable_doc_title)) {
          an_object[an_object_key] = an_object[an_object_key]
            .replaceFirst(rgx.variable_doc_title,
              conf_make_meta.meta.title_full);
        }
        collapsed_lev["h0"] = 0;
        an_object["lev_collapsed_number"]
          = collapsed_lev["h0"].to!string;
        lv["lv"] = DocStructMarkupHeading.h_sect_A;
        ++lv["h0"];
        lv["h1"] = eN.bi.off;
        lv["h2"] = eN.bi.off;
        lv["h3"] = eN.bi.off;
        lv["h4"] = eN.bi.off;
        lv["h5"] = eN.bi.off;
        lv["h6"] = eN.bi.off;
        lv["h7"] = eN.bi.off;
        goto default;
      case "B":
        collapsed_lev["h1"] = collapsed_lev["h0"] + 1;
        an_object["lev_collapsed_number"]
          = collapsed_lev["h1"].to!string;
        lv["lv"] = DocStructMarkupHeading.h_sect_B;
        ++lv["h1"];
        lv["h2"] = eN.bi.off;
        lv["h3"] = eN.bi.off;
        lv["h4"] = eN.bi.off;
        lv["h5"] = eN.bi.off;
        lv["h6"] = eN.bi.off;
        lv["h7"] = eN.bi.off;
        goto default;
      case "C":
        collapsed_lev["h2"] = collapsed_lev["h1"] + 1;
        an_object["lev_collapsed_number"]
          = collapsed_lev["h2"].to!string;
        lv["lv"] = DocStructMarkupHeading.h_sect_C;
        ++lv["h2"];
        lv["h3"] = eN.bi.off;
        lv["h4"] = eN.bi.off;
        lv["h5"] = eN.bi.off;
        lv["h6"] = eN.bi.off;
        lv["h7"] = eN.bi.off;
        goto default;
      case "D":
        collapsed_lev["h3"] = collapsed_lev["h2"] + 1;
        an_object["lev_collapsed_number"]
          = collapsed_lev["h3"].to!string;
        lv["lv"] = DocStructMarkupHeading.h_sect_D;
        ++lv["h3"];
        lv["h4"] = eN.bi.off;
        lv["h5"] = eN.bi.off;
        lv["h6"] = eN.bi.off;
        lv["h7"] = eN.bi.off;
        goto default;
      case "1":
        if (lv["h3"] > eN.bi.off) {
          collapsed_lev["h4"] = collapsed_lev["h3"] + 1;
        } else if (lv["h2"] > eN.bi.off) {
          collapsed_lev["h4"] = collapsed_lev["h2"] + 1;
        } else if (lv["h1"] > eN.bi.off) {
          collapsed_lev["h4"] = collapsed_lev["h1"] + 1;
        } else if (lv["h0"] > eN.bi.off) {
          collapsed_lev["h4"] = collapsed_lev["h0"] + 1;
        }
        an_object["lev_collapsed_number"]
          = collapsed_lev["h4"].to!string;
        lv["lv"] = DocStructMarkupHeading.h_text_1;
        ++lv["h4"];
        lv["h5"] = eN.bi.off;
        lv["h6"] = eN.bi.off;
        lv["h7"] = eN.bi.off;
        goto default;
      case "2":
        if (lv["h5"] > eN.bi.off) {
          an_object["lev_collapsed_number"]
            = collapsed_lev["h5"].to!string;
        } else if (lv["h4"] > eN.bi.off) {
          collapsed_lev["h5"] = collapsed_lev["h4"] + 1;
          an_object["lev_collapsed_number"]
            = collapsed_lev["h5"].to!string;
        }
        lv["lv"] = DocStructMarkupHeading.h_text_2;
        ++lv["h5"];
        lv["h6"] = eN.bi.off;
        lv["h7"] = eN.bi.off;
        goto default;
      case "3":
        if (lv["h6"] > eN.bi.off) {
          an_object["lev_collapsed_number"]
            = collapsed_lev["h6"].to!string;
        } else if (lv["h5"] > eN.bi.off) {
          collapsed_lev["h6"] = collapsed_lev["h5"] + 1;
          an_object["lev_collapsed_number"]
            = collapsed_lev["h6"].to!string;
        }
        lv["lv"] = DocStructMarkupHeading.h_text_3;
        ++lv["h6"];
        lv["h7"] = eN.bi.off;
        goto default;
      case "4":
        if (lv["h7"] > eN.bi.off) {
          an_object["lev_collapsed_number"]
            = collapsed_lev["h7"].to!string;
        } else if (lv["h6"] > eN.bi.off) {
          collapsed_lev["h7"] = collapsed_lev["h6"] + 1;
          an_object["lev_collapsed_number"]
            = collapsed_lev["h7"].to!string;
        }
        lv["lv"] = DocStructMarkupHeading.h_text_4;
        ++lv["h7"];
        goto default;
      default:
        an_object["lev_markup_number"] = lv["lv"].to!string;
      }
      an_object["dummy_heading_status"] = (pith["dummy_heading_status"] == eN.bi.off) ? "f" : "t";
      debug(heading) { writeln(line.strip); }
    }
    struct ST_flow_heading_matched {
      string[string]  this_object;
      int[string]     line_occur;
      string          an_object_key;
      int[string]     lv;
      int[string]     collapsed_lev;
      uint[string]    pith;
      CMM             conf_make_meta;
    }
    ST_flow_heading_matched ret;
    {
      ret.this_object    = an_object;
      ret.line_occur     = line_occur;
      ret.an_object_key  = an_object_key;
      ret.lv             = lv;
      ret.collapsed_lev  = collapsed_lev;
      ret.pith           = pith;
      ret.conf_make_meta = conf_make_meta;
    }
    return ret;
  }
  // ↑ - heading
  // ↓ - table
  ObjGenericComposite flow_table_instructions(H)(
    ObjGenericComposite  table_object,
    H                    table_head,
  ) {
    static auto rgx = RgxI();
    table_object.metainfo.is_of_part        = "body";
    table_object.metainfo.is_of_section     = "body";
    table_object.metainfo.is_of_type        = "block";
    table_object.metainfo.is_a              = "table";
    table_object.has.inline_notes_reg       = false;
    table_object.has.inline_notes_star      = false;
    table_object.has.inline_links           = false;
    if (auto m = table_head.matchFirst(rgx.table_head_instructions)) {
      table_object.table.heading
        = ((m["c_heading"].length > 0) && (m["c_heading"] == "h")) ? true : false;
      table_object.table.number_of_columns
        = ((m["c_num"].length > 0) && (m["c_num"].to!int > 0)) ? m["c_num"].to!int : 0;
      foreach (cw; m["c_widths"].matchAll(rgx.table_col_widths)) {
        auto x = cw.hit.matchFirst(rgx.table_col_widths_and_alignment);
        table_object.table.column_widths ~= x["width"].to!int;
        table_object.table.column_aligns ~= (x["align"].empty) ? "" : x["align"];
      }
    }
    return table_object;
  }
  ST_flow_table_array_munge flow_table_array_munge()(
    ObjGenericComposite  table_object,
    string[][]           table_array,
  ) {
    static auto rgx = RgxI();
    static auto mng = InlineMarkup();
    string _table_substantive;
    ulong col_num;
    ulong col_num_;
    ulong col_num_chk = 0;
    foreach(idx_r, row; table_array) {
      debug(table_dev) { writeln("row ", idx_r); }
      col_num_ = 0;
      if (col_num == 0
      || col_num < row.length) {
        col_num = row.length;
      }
      if (col_num_chk == 0) {
        col_num_chk = col_num;
      } else if (col_num == 1) {
        debug(table_dev) { writeln("table note: "); }
      } else if (col_num_chk != col_num) {
        debug(table_dev) { writeln("warning irregular number of columns: ", col_num_chk, " != ", col_num); }
      } else {
      }
      foreach(idx_c, col; row) {
        debug(table_dev) { write(idx_c, ", "); }
        col_num_ = idx_c;
        _table_substantive ~= col ~ mng.tc_s;
        if (idx_r == 0 && table_object.table.heading) {
        } else if (col.match(rgx.numeric_col) && idx_r == 1) { // conditions reversed to avoid: gdc compiled program run segfault
          if ((table_object.table.column_aligns.length > idx_c)
          && (table_object.table.column_aligns[idx_c].matchFirst(rgx.table_col_align_match))) {
            table_object.table.column_aligns[idx_c] = table_object.table.column_aligns[idx_c];
          } else if (table_object.table.column_aligns.length > idx_c) {
            table_object.table.column_aligns[idx_c] = "r";
          } else {
            table_object.table.column_aligns ~= "r";
          }
        } else if (idx_r == 1) {
          if ((table_object.table.column_aligns.length > idx_c)
          && (table_object.table.column_aligns[idx_c].matchFirst(rgx.table_col_align_match))) {
            table_object.table.column_aligns[idx_c] = table_object.table.column_aligns[idx_c];
          } else if (table_object.table.column_aligns.length > idx_c) {
            table_object.table.column_aligns[idx_c] = "l";
          } else {
            table_object.table.column_aligns ~= "l";
          }
        }
      }
      debug(table_dev) { writeln(""); }
      if (col_num_chk > 0 && (col_num != col_num_chk)) {
      } else if (col_num == col_num_chk){
      } else {
        col_num_chk = col_num;
      }
      _table_substantive = _table_substantive.replaceFirst(rgx.table_col_separator_nl, "\n");
    }
    if (table_object.table.number_of_columns != col_num) {
      if (table_object.table.number_of_columns == 0) {
        table_object.table.number_of_columns = (col_num).to!int;
      } else {
        debug(table_dev) { writeln(table_object.table.number_of_columns, " != ", col_num); }
      }
    }
    if (table_object.table.number_of_columns == 0
    && table_object.table.column_widths.length > 0) {
      writeln(__LINE__, " ERROR");
    }
    if (table_object.table.number_of_columns > 0
    && table_object.table.column_widths.length == 0) {
      double col_w = (100.00 / table_object.table.number_of_columns);
      foreach (i; 0..table_object.table.number_of_columns) {
        table_object.table.column_widths ~= col_w;
      }
    } else if (table_object.table.number_of_columns
    != table_object.table.column_widths.length) {
      debug(table_dev) { writeln(m.hit); } // further logic required
      if (table_object.table.number_of_columns > table_object.table.column_widths.length) {
        double col_w = (100.00 - (table_object.table.column_widths).sum)
          / (table_object.table.number_of_columns - table_object.table.column_widths.length);
        foreach (i; 0..table_object.table.column_widths.length) {
          table_object.table.column_widths ~= col_w;
        }
        foreach (i; 0..(table_object.table.number_of_columns - table_object.table.column_widths.length)) {
          table_object.table.column_widths ~= col_w;
        }
      } else if (table_object.table.number_of_columns < table_object.table.column_widths.length) {
        writeln(__LINE__, " warning, ERROR");
      }
    }
    if (table_object.table.column_widths.sum > 101
    || table_object.table.column_widths.sum < 95 ) {
      writeln("sum: ", table_object.table.column_widths.sum,
        ", array: ", table_object.table.column_widths,
        ", cols: ", table_object.table.number_of_columns);
      writeln(_table_substantive);
    }
    debug(table_res) {
      writeln("aligns: ", table_object.table.column_aligns, "\n",
        "no. of columns: ", table_object.table.number_of_columns, "\n",
        "col widths: ", table_object.table.column_widths,
          " sum: ", table_object.table.column_widths.sum, "\n",
        _table_substantive);
    }
    table_object.text = _table_substantive;
    ST_flow_table_array_munge ret;
    {
      ret.table_object      = table_object;
      ret.table_array       = table_array;
    }
    return ret;
  }
  @system ST_flow_table_substantive_munge flow_table_substantive_munge()(
    ObjGenericComposite  table_object,
    string               table_substantive,
  ) {
    static auto rgx = RgxI();
    static auto munge = ObjInlineMarkupMunge();
    string[] _table_rows = (table_substantive).split(rgx.table_row_delimiter);
    string[] _table_cols;
    string[][] _table_array;
    foreach(col; _table_rows) {
      _table_cols = col.split(rgx.table_col_delimiter);
      _table_array ~= _table_cols;
    }
    {
      auto _get = table_object.flow_table_array_munge(_table_array);
      {
        table_object = _get.table_object;
        _table_array = _get.table_array; // what do you do with this? how is this passed down?
      }
    }
    ST_flow_table_substantive_munge ret;
    {
      ret.table_object      = table_object;
      ret.table_substantive = table_substantive; // has anything been changed here?
    }
    return ret;
  }
  @system ST_flow_table_substantive_munge flow_table_substantive_munge_special()(
    ObjGenericComposite  table_object,
    string               table_substantive,
  ) {
    static auto rgx = RgxI();
    static auto munge = ObjInlineMarkupMunge();
    string[] _table_rows = (table_substantive).split(rgx.table_row_delimiter_special);
    string[] _table_cols;
    string[][] _table_array;
    foreach(col; _table_rows) {
      _table_cols = col.split(rgx.table_col_delimiter_special);
      _table_array ~= _table_cols;
    }
    {
      auto _get = table_object.flow_table_array_munge(_table_array);
      {
        table_object = _get.table_object;
        _table_array = _get.table_array;
      }
    }
    ST_flow_table_substantive_munge ret;
    {
      ret.table_object      = table_object;
      ret.table_substantive = table_substantive;
    }
    return ret;
  }
  @system ST_flow_table_closed_make_special_notation_table flow_table_closed_make_special_notation_table_(CMM)(
    char[]                line,
    string[string]        an_object,
    ObjGenericComposite[] the_document_body_section,
    OCNset                obj_cite_digits,
    ObjGenericComposite   comp_obj_,
    int                   cntr,
    uint[string]          pith,
    CMM                   conf_make_meta
  ) {
    comp_obj_       = comp_obj_.init;
    obj_cite_digits = ocn_emit(pith["ocn"]);
    auto comp_obj_location = node_construct.node_location_emitter(
        content_non_header,
        tag_in_seg,
        lev_anchor_tag,
        tag_assoc,
        obj_cite_digits,
        cntr,
        heading_ptr-1,
        "table"
      );
    an_object["is"]                                             = "table";
    ST_txtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_struct
      = obj_im.obj_inline_markup_and_anchor_tags_and_misc(an_object, "body_nugget", conf_make_meta, No._new_doc);
    an_object["substantive"]                                    = substantive_obj_misc_struct.obj_txt;
    comp_obj_.metainfo.ocn                                      = obj_cite_digits.object_number;
    comp_obj_.metainfo.identifier                               = obj_cite_digits.identifier;
    comp_obj_.metainfo.object_number_off                        = obj_cite_digits.off;
    comp_obj_.tags.html_segment_anchor_tag_is                   = tag_in_seg["seg_lv4"];
    comp_obj_.tags.epub_segment_anchor_tag_is                   = tag_in_seg["seg_lv1to4"];
    comp_obj_.metainfo.o_n_book_index                           = obj_cite_digits.bkidx;
    comp_obj_.metainfo.object_number_type                       = obj_cite_digits.type;
    comp_obj_                                                   = comp_obj_.flow_table_instructions(an_object["table_head"]);
    {
      auto _get = comp_obj_.flow_table_substantive_munge_special(an_object["substantive"]);
      {
        comp_obj_           = _get.table_object;
        an_object["substantive"] = _get.table_substantive;
      }
    }
    the_document_body_section                                   ~= comp_obj_;
    object_reset(an_object);
    processing.remove("verse");
    ++cntr;
    ST_flow_table_closed_make_special_notation_table ret;
    {
      ret.this_object               = an_object;
      ret.the_document_body_section = the_document_body_section;
      ret.obj_cite_digits           = obj_cite_digits;
      ret.comp_obj_                 = comp_obj_;
      ret.cntr                      = cntr;
      ret.pith                      = pith;
    }
    return ret;
  }
  // ↑ - table
  
  @system ST_flow_block_flag_line_empty flow_block_flag_line_empty_(B,CMM,Ts)(
    char[]                   line,
    string[string]           an_object,
    B                        bookindex_extract_hash,
    ObjGenericComposite[]    the_document_body_section,
    string[][string][string] bookindex_unordered_hashes,
    OCNset                   obj_cite_digits,
    ObjGenericComposite      comp_obj_,
    int                      cntr,
    uint[string]             pith,
    string[string]           object_number_poem,
    CMM                      conf_make_meta,
    Ts                       tag_in_seg,
  ) {
    assert(
      line.empty,
      "\nline should be empty:\n  \""
      ~ line ~ "\""
    );
    assert(
      (pith["block_state"] == eN.blk_state.closing),
      "code block status: closed"
    );
    static auto rgx = RgxI();
    if (pith["block_state"] == eN.blk_state.closing) {
      if (pith["block_is"] == eN.blk_is.quote) {
        obj_cite_digits = ocn_emit(pith["ocn"]);
        an_object["bookindex_nugget"]
          = ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
        bookindex_unordered_hashes
          = bookindex_extract_hash.bookindex_nugget_hash(
            an_object["bookindex_nugget"],
            obj_cite_digits,
            tag_in_seg
          );
        an_object["is"]                                         = "quote";
        auto comp_obj_location
          = node_construct.node_location_emitter(
            content_non_header,
            tag_in_seg,
            lev_anchor_tag,
            tag_assoc,
            obj_cite_digits,
            cntr,
            heading_ptr-1,
            an_object["is"]
          );
        ST_txtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_struct
          = obj_im.obj_inline_markup_and_anchor_tags_and_misc(an_object, an_object_key, conf_make_meta, No._new_doc);
        an_object["substantive"]                                = substantive_obj_misc_struct.obj_txt;
        anchor_tag                                              = substantive_obj_misc_struct.anchor_tag;
        comp_obj_                                               = set_object_generic("body", "body", "block", "quote", an_object["substantive"], obj_cite_digits.object_number);
        comp_obj_.metainfo.identifier                           = obj_cite_digits.identifier;
        comp_obj_.metainfo.object_number_off                    = obj_cite_digits.off;
        comp_obj_.metainfo.o_n_book_index                       = obj_cite_digits.bkidx;
        comp_obj_.metainfo.object_number_type                   = obj_cite_digit_type;
        comp_obj_.metainfo.lang                                 = an_object["lang"];
        comp_obj_.metainfo.attrib                               = an_object["attrib"];
        comp_obj_.tags.html_segment_anchor_tag_is               = tag_in_seg["seg_lv4"];
        comp_obj_.tags.epub_segment_anchor_tag_is               = tag_in_seg["seg_lv1to4"];
        comp_obj_.has.inline_notes_reg                          = substantive_obj_misc_struct.has_notes_reg;
        comp_obj_.has.inline_notes_star                         = substantive_obj_misc_struct.has_notes_star;
        comp_obj_.has.inline_links                              = substantive_obj_misc_struct.has_links;
        the_document_body_section                               ~= comp_obj_;
        tag_assoc                                               = an_object.inline_para_link_anchor(tag_in_seg, tag_assoc);
        pith["block_is"]                                        = eN.blk_is.quote;
        pith["block_state"]                                     = eN.blk_state.off;
        pith["block_delim"]                                     = eN.blk_delim.off;
        object_reset(an_object);
        processing.remove("verse");
        ++cntr;
      } else if (pith["block_is"] == eN.blk_is.group) {
        obj_cite_digits = ocn_emit(pith["ocn"]);
        an_object["bookindex_nugget"]
          = ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
        bookindex_unordered_hashes
          = bookindex_extract_hash.bookindex_nugget_hash(
            an_object["bookindex_nugget"],
            obj_cite_digits,
            tag_in_seg
          );
        an_object["is"]                                         = "group";
        auto comp_obj_location
          = node_construct.node_location_emitter(
            content_non_header,
            tag_in_seg,
            lev_anchor_tag,
            tag_assoc,
            obj_cite_digits,
            cntr,
            heading_ptr-1,
            an_object["is"]
          );
        ST_txtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_struct
          = obj_im.obj_inline_markup_and_anchor_tags_and_misc(an_object, an_object_key, conf_make_meta, No._new_doc);
        an_object["substantive"]                                = substantive_obj_misc_struct.obj_txt;
        anchor_tag                                              = substantive_obj_misc_struct.anchor_tag;
        comp_obj_                                               = set_object_generic("body", "body", "block", "group", an_object["substantive"], obj_cite_digits.object_number);
        comp_obj_.metainfo.identifier                           = obj_cite_digits.identifier;
        comp_obj_.metainfo.object_number_off                    = obj_cite_digits.off;
        comp_obj_.metainfo.o_n_book_index                       = obj_cite_digits.bkidx;
        comp_obj_.metainfo.object_number_type                   = obj_cite_digits.type;
        comp_obj_.metainfo.lang                                 = an_object["lang"];
        comp_obj_.metainfo.attrib                               = an_object["attrib"];
        comp_obj_.tags.html_segment_anchor_tag_is               = tag_in_seg["seg_lv4"];
        comp_obj_.tags.epub_segment_anchor_tag_is               = tag_in_seg["seg_lv1to4"];
        comp_obj_.has.inline_notes_reg                          = substantive_obj_misc_struct.has_notes_reg;
        comp_obj_.has.inline_notes_star                         = substantive_obj_misc_struct.has_notes_star;
        comp_obj_.has.inline_links                              = substantive_obj_misc_struct.has_links;
        the_document_body_section                               ~= comp_obj_;
        tag_assoc                                               = an_object.inline_para_link_anchor(tag_in_seg, tag_assoc);
        pith["block_is"]                                        = eN.blk_is.poem;
        pith["block_state"]                                     = eN.blk_state.off;
        pith["block_delim"]                                     = eN.blk_delim.off;
        object_reset(an_object);
        processing.remove("verse");
        ++cntr;
      } else if (pith["block_is"] == eN.blk_is.block) {
        obj_cite_digits = ocn_emit(pith["ocn"]);
        an_object["bookindex_nugget"]
          = ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
        bookindex_unordered_hashes
          = bookindex_extract_hash.bookindex_nugget_hash(
            an_object["bookindex_nugget"],
            obj_cite_digits,
            tag_in_seg
          );
        an_object["is"]                                         = "block";
        auto comp_obj_location
          = node_construct.node_location_emitter(
            content_non_header,
            tag_in_seg,
            lev_anchor_tag,
            tag_assoc,
            obj_cite_digits,
            cntr,
            heading_ptr-1,
            an_object["is"]
          );
        ST_txtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_struct
          = obj_im.obj_inline_markup_and_anchor_tags_and_misc(an_object, an_object_key, conf_make_meta, No._new_doc);
        an_object["substantive"]                                = substantive_obj_misc_struct.obj_txt;
        // anchor_tag                                           = substantive_obj_misc_struct.anchor_tag; // check
        comp_obj_                                               = set_object_generic("body", "body", "block", "block", an_object["substantive"], obj_cite_digits.object_number);
        comp_obj_.metainfo.identifier                           = obj_cite_digits.identifier;
        comp_obj_.metainfo.object_number_off                    = obj_cite_digits.off;
        comp_obj_.metainfo.o_n_book_index                       = obj_cite_digits.bkidx;
        comp_obj_.metainfo.object_number_type                   = obj_cite_digit_type;
        comp_obj_.metainfo.lang                                 = an_object["lang"];
        comp_obj_.metainfo.attrib                               = an_object["attrib"];
        comp_obj_.tags.html_segment_anchor_tag_is               = tag_in_seg["seg_lv4"];
        comp_obj_.tags.epub_segment_anchor_tag_is               = tag_in_seg["seg_lv1to4"];
        comp_obj_.has.inline_notes_reg                          = substantive_obj_misc_struct.has_notes_reg;
        comp_obj_.has.inline_notes_star                         = substantive_obj_misc_struct.has_notes_star;
        comp_obj_.has.inline_links                              = substantive_obj_misc_struct.has_links;
        the_document_body_section                               ~= comp_obj_;
        pith["block_is"]                                        = eN.blk_is.block;
        pith["block_state"]                                     = eN.blk_state.off;
        pith["block_delim"]                                     = eN.blk_delim.off;
        object_reset(an_object);
        processing.remove("verse");
        ++cntr;
      } else if (pith["block_is"] == eN.blk_is.poem) {
        an_object["bookindex_nugget"]
          = ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
        bookindex_unordered_hashes
          = bookindex_extract_hash.bookindex_nugget_hash(
            an_object["bookindex_nugget"],
            obj_cite_digits,
            tag_in_seg
          );
        an_object["is"]                                         = "verse";
        auto comp_obj_location
          = node_construct.node_location_emitter(
            content_non_header,
            tag_in_seg,
            lev_anchor_tag,
            tag_assoc,
            obj_cite_digits,
            cntr,
            heading_ptr-1,
            an_object["is"]
          );
        comp_obj_poem_ocn                                       = set_object_generic("body", "body", "block", "poem", "", obj_cite_digits.object_number);
        comp_obj_poem_ocn.metainfo.identifier                   = obj_cite_digits.identifier;
        comp_obj_poem_ocn.metainfo.object_number_off            = obj_cite_digits.off;
        comp_obj_poem_ocn.metainfo.o_n_book_index               = obj_cite_digits.bkidx;
        comp_obj_poem_ocn.metainfo.object_number_type           = obj_cite_digits.type;
        the_document_body_section                               ~= comp_obj_poem_ocn;
        pith["block_is"]                                        = eN.blk_is.poem;
        pith["block_state"]                                     = eN.blk_state.off;
        pith["block_delim"]                                     = eN.blk_delim.off;
        object_reset(an_object);
        processing.remove("verse");
      } else if (pith["block_is"] == eN.blk_is.code) {
        obj_cite_digits = ocn_emit(pith["ocn"]);
        an_object["bookindex_nugget"]
          = ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
        bookindex_unordered_hashes
          = bookindex_extract_hash.bookindex_nugget_hash(
            an_object["bookindex_nugget"],
            obj_cite_digits,
            tag_in_seg
          );
        an_object["is"]                                         = "code";
        auto comp_obj_location
          = node_construct.node_location_emitter(
            content_non_header,
            tag_in_seg,
            lev_anchor_tag,
            tag_assoc,
            obj_cite_digits,
            cntr,
            heading_ptr-1,
            an_object["is"]
          );
        ST_txtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_struct
          = obj_im.obj_inline_markup_and_anchor_tags_and_misc(an_object, an_object_key, conf_make_meta, No._new_doc);
        an_object["substantive"]                                = substantive_obj_misc_struct.obj_txt;
        anchor_tag                                              = substantive_obj_misc_struct.anchor_tag;
        comp_obj_                                               = set_object_generic("body", "body", "block", "code", an_object["substantive"], obj_cite_digits.object_number);
        comp_obj_.metainfo.identifier                           = obj_cite_digits.identifier;
        comp_obj_.metainfo.object_number_off                    = obj_cite_digits.off;
        comp_obj_.metainfo.o_n_book_index                       = obj_cite_digits.bkidx;
        comp_obj_.metainfo.object_number_type                   = obj_cite_digits.type;
        comp_obj_.metainfo.syntax                               = an_object["syntax"];
        comp_obj_.metainfo.attrib                               = an_object["attrib"];
        comp_obj_.code_block.linenumbers                        = (an_object["attrib"].match(rgx.code_numbering)) ? true : false;
        comp_obj_.tags.html_segment_anchor_tag_is               = tag_in_seg["seg_lv4"];
        comp_obj_.tags.epub_segment_anchor_tag_is               = tag_in_seg["seg_lv1to4"];
        comp_obj_.has.inline_notes_reg                          = substantive_obj_misc_struct.has_notes_reg;
        comp_obj_.has.inline_notes_star                         = substantive_obj_misc_struct.has_notes_star;
        comp_obj_.has.inline_links                              = substantive_obj_misc_struct.has_links;
        the_document_body_section                               ~= comp_obj_;
        pith["block_is"]                                        = eN.blk_is.code;
        pith["block_state"]                                     = eN.blk_state.off;
        pith["block_delim"]                                     = eN.blk_delim.off;
        object_reset(an_object);
        processing.remove("verse");
        ++cntr;
      } else if (pith["block_is"]    == eN.blk_is.table) {
        comp_obj_ = comp_obj_.init;
        obj_cite_digits = ocn_emit(pith["ocn"]);
        an_object["bookindex_nugget"]
          = ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
        bookindex_unordered_hashes
          = bookindex_extract_hash.bookindex_nugget_hash(
            an_object["bookindex_nugget"],
            obj_cite_digits,
            tag_in_seg
          );
        an_object["is"]                                         = "table";
        auto comp_obj_location
          = node_construct.node_location_emitter(
            content_non_header,
            tag_in_seg,
            lev_anchor_tag,
            tag_assoc,
            obj_cite_digits,
            cntr,
            heading_ptr-1,
            an_object["is"]
          );
        ST_txtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_struct
          = obj_im.obj_inline_markup_and_anchor_tags_and_misc(an_object, an_object_key, conf_make_meta, No._new_doc);
        an_object["substantive"]                                = substantive_obj_misc_struct.obj_txt;
        comp_obj_                                               = comp_obj_.init;
        comp_obj_.metainfo.ocn                                  = obj_cite_digits.object_number;
        comp_obj_.metainfo.identifier                           = obj_cite_digits.identifier;
        comp_obj_.metainfo.object_number_off                    = obj_cite_digits.off;
        comp_obj_.tags.html_segment_anchor_tag_is               = tag_in_seg["seg_lv4"];
        comp_obj_.tags.epub_segment_anchor_tag_is               = tag_in_seg["seg_lv1to4"];
        comp_obj_.metainfo.o_n_book_index                       = obj_cite_digits.bkidx;
        comp_obj_.metainfo.object_number_type                   = obj_cite_digits.type;
        comp_obj_                                               = comp_obj_.flow_table_instructions(an_object["table_head"]);
        {
          auto _get = comp_obj_.flow_table_substantive_munge(an_object["substantive"]);
          {
            comp_obj_           = _get.table_object;
            an_object["substantive"] = _get.table_substantive;
          }
        }
        the_document_body_section                               ~= comp_obj_;
        pith["block_is"]                                        = eN.blk_is.table;
        pith["block_state"]                                     = eN.blk_state.off;
        pith["block_delim"]                                     = eN.blk_delim.off;
        object_reset(an_object);
        processing.remove("verse");
        ++cntr;
      }
    }
    ST_flow_block_flag_line_empty ret;
    {
      ret.this_object                = an_object;
      ret.the_document_body_section  = the_document_body_section;
      ret.bookindex_unordered_hashes = bookindex_unordered_hashes;
      ret.obj_cite_digits            = obj_cite_digits;
      ret.comp_obj_                  = comp_obj_; //
      ret.cntr                       = cntr;
      ret.pith                       = pith;
    }
    return ret;
  }
  // ↓ - object set
  ObjGenericComposite set_object_heading()(
    string level,
    string part,
    string section,
    string text,
  ) {
    ObjGenericComposite comp_obj;
    {
      comp_obj                                                = comp_obj.init;
      comp_obj.metainfo.is_of_part                            = part;
      comp_obj.metainfo.is_of_section                         = section;
      comp_obj.metainfo.is_of_type                            = "para";
      comp_obj.metainfo.is_a                                  = "heading";
      comp_obj.text                                           = text;
      comp_obj.metainfo.ocn                                   = 0;
      if (level == "lev1") {
        comp_obj.metainfo.heading_lev_markup                  = 1;
        comp_obj.metainfo.heading_lev_collapsed               = 1;
        comp_obj.metainfo.parent_ocn                          = 1;
        comp_obj.metainfo.parent_lev_markup                   = 0;
      } else if (level == "lev4") {
        comp_obj.metainfo.heading_lev_markup                  = 4;
        comp_obj.metainfo.heading_lev_collapsed               = 1;
        comp_obj.metainfo.parent_ocn                          = 1;
        comp_obj.metainfo.parent_lev_markup                   = 0;
        comp_obj.metainfo.dom_structure_markedup_tags_status  = [ 1, 1, 0, 0, 1, 0, 0, 0];
        comp_obj.metainfo.dom_structure_collapsed_tags_status = [ 1, 1, 1, 0, 0, 0, 0, 0];
      }
    }
    return comp_obj;
  }
  ObjGenericComposite set_object_generic()(
    string part,
    string section,
    string type,
    string is_a,
    string text,
    int ocn,
  ) {
    ObjGenericComposite comp_obj;
    {
      comp_obj                                                = comp_obj.init;
      comp_obj.metainfo.is_of_part                            = part;
      comp_obj.metainfo.is_of_section                         = section;
      comp_obj.metainfo.is_of_type                            = type;
      comp_obj.metainfo.is_a                                  = is_a;
      comp_obj.text                                           = text;
      comp_obj.metainfo.ocn                                   = ocn;
    }
    return comp_obj;
  }
  // ↑ - object set
  // ↓ - object inline munge
  static struct ObjInlineMarkupMunge {
    string[string] obj_txt;
    int n_foot, n_foot_reg, n_foot_sp_asterisk, n_foot_sp_plus;
    string asterisks_, plus_;
    string obj_txt_out, tail, note;
    static auto rgx = RgxI();
    static auto mkup = InlineMarkup();
    int stage_reset_note_numbers = true;
    private auto initialize_note_numbers() {
      n_foot                          = 0;
      n_foot_reg                      = 0;
      n_foot_sp_asterisk              = 0;
      n_foot_sp_plus                  = 0;
    }
    static auto images()(string obj_txt_in) {
      static auto mng = InlineMarkup();
      // url matched
      obj_txt_in = obj_txt_in.replaceAll(rgx.inline_notes_al_special, ""); // TODO reinstate when special footnotes are implemented
      if (obj_txt_in.match(rgx.smid_image_generic)) {                            // images with and without links
        debug(images) { writeln("Image: ", obj_txt_in); }
        if (obj_txt_in.match(rgx.smid_image_with_dimensions)) {
          obj_txt_in = obj_txt_in
            .replaceAll(rgx.smid_image_with_dimensions, ("$1" ~ mkup.img ~ "$2,w$3h$4 " ~ "$5"))
            .replaceAll(rgx.smid_image_delimit, ("$1"
              ~ mkup.lnk_o ~ "$2".strip ~ mkup.lnk_c
              ~ mkup.url_o ~ mkup.url_c));
          debug(images) { writeln("IMAGE with size: ", obj_txt_in); }
        } else if (obj_txt_in.match(rgx.smid_image)) {
          obj_txt_in = obj_txt_in
            .replaceAll(rgx.smid_image, ("$1" ~ mkup.img ~ "$2,w0h0" ~ "$3"))
            .replaceAll(rgx.smid_image_delimit, ("$1"
              ~ mkup.lnk_o ~ "$2".strip ~ mkup.lnk_c
              ~ mkup.url_o ~ mkup.url_c));
          debug(images) { writeln("IMAGE: ", obj_txt_in); } // decide on representation
        }
      }
      return obj_txt_in;
    }
    ST_txtPlusHasFootnotes footnotes_endnotes_markup_and_number_or_stars()(string obj_txt_in, bool reset_note_numbers) {
      // endnotes (regular)
      bool flg_notes_reg  = false;
      bool flg_notes_star = false;
      bool flg_notes_plus = false;
      obj_txt_in = obj_txt_in.replaceAll(
        rgx.inline_notes_curly,
        (mkup.en_a_o ~ " $1" ~ mkup.en_a_c)
      );
      if (!(stage_reset_note_numbers) && reset_note_numbers) {
        stage_reset_note_numbers = true;
      }
      obj_txt_out = "";
      if (obj_txt_in.match(rgx.inline_notes_al_gen)) {
        string[] _tmp_txt;
        foreach (x; obj_txt_in.split("\n")) {
          if (auto m = x.matchAll(rgx.inline_text_and_note_al_)) {
            if (stage_reset_note_numbers) {
              n_foot                  = 0;
              n_foot_reg              = 0;
              n_foot_sp_asterisk      = 0;
              n_foot_sp_plus          = 0;
            }
            stage_reset_note_numbers = false;
            foreach(n; m) {
              if (n.hit.to!string.match(rgx.inline_al_delimiter_open_symbol_star)) {
                flg_notes_star =  true;
                ++n_foot_sp_asterisk;
                asterisks_ = "*";
                n_foot = n_foot_sp_asterisk;
                _tmp_txt ~= n.hit.to!string.replaceFirst(
                  rgx.inline_al_delimiter_open_symbol_star,
                  (mkup.en_a_o ~ replicate(asterisks_, n_foot_sp_asterisk) ~ " ")
                );
              } else if (n.hit.to!string.match(rgx.inline_al_delimiter_open_symbol_plus)) {
                flg_notes_plus =  true;
                ++n_foot_sp_plus;
                plus_ = "*";
                n_foot = n_foot_sp_plus;
                _tmp_txt ~= n.hit.to!string.replaceFirst(
                  rgx.inline_al_delimiter_open_symbol_plus,
                  (mkup.en_a_o ~ replicate(plus_, n_foot_sp_plus) ~ " ")
                );
              } else if (n.hit.to!string.matchFirst(rgx.inline_al_delimiter_open_regular)) {
                string _tmp_str = n.hit.to!string;
                flg_notes_reg =  true;
                foreach (q; n.hit.to!string.matchAll(rgx.inline_al_delimiter_open_regular)) {
                  ++n_foot_reg;
                  n_foot = n_foot_reg;
                  _tmp_str = replaceFirst!(m => mkup.en_a_o ~ n_foot.to!string ~ " ")
                    (_tmp_str, rgx.inline_al_delimiter_open_regular);
                }
                _tmp_txt ~= _tmp_str;
              } else {
                _tmp_txt ~= n.hit.to!string;
              }
            }
            obj_txt_out = _tmp_txt.join("\n");
          }
        }
      } else {
        obj_txt_out = obj_txt_in;
      }
      ST_txtPlusHasFootnotes ret;
      {
        ret.obj_txt            = obj_txt_out;
        ret.has_notes_reg      = flg_notes_reg;
        ret.has_notes_star     = flg_notes_star;
        ret.has_notes_plus     = flg_notes_plus;
      }
      return ret;
    }
    private ST_txtPlusHasFootnotesUrlsImages object_notes_and_links_()(
      string obj_txt_in,
      bool reset_note_numbers = false
    ) {
      obj_txt_out = "";
      bool urls = false;
      bool images_without_dimensions = false;
      tail = "";
      // special endnotes
      obj_txt_in = obj_txt_in.replaceAll(
        rgx.inline_notes_curly_sp_asterisk,
        (mkup.en_a_o ~ "*" ~ " $1" ~ mkup.en_a_c)
      );
      obj_txt_in
        = obj_txt_in.replaceAll(
          rgx.inline_notes_curly_sp_plus,
          (mkup.en_a_o ~ "+" ~ " $1" ~ mkup.en_a_c)
        );
      // image matched
      if (obj_txt_in.match(rgx.smid_image_generic)) {
        obj_txt_in = images(obj_txt_in);
        if (obj_txt_in.match(rgx.smid_mod_image_without_dimensions)) {
          images_without_dimensions = true;
        }
      }
      // url matched
      if (obj_txt_in.match(rgx.smid_inline_url)) {
        urls = true;
        obj_txt_in = obj_txt_in.links_and_images;
      }
      if (auto m = obj_txt_in.match(rgx.para_inline_link_anchor)) {
        obj_txt_in = obj_txt_in
          .replaceAll(rgx.para_inline_link_anchor, "┃$1┃");
      }
      ST_txtPlusHasFootnotes ftn = footnotes_endnotes_markup_and_number_or_stars(obj_txt_in, reset_note_numbers);
      obj_txt_out = ftn.obj_txt;
      debug(footnotes) { writeln(obj_txt_out, tail); }
      obj_txt_out = obj_txt_out ~ tail;
      debug(footnotesdone) {
        foreach(m; matchAll(obj_txt_out, (mkup.en_a_o ~ `\s*(.+?)` ~ mkup.en_a_c))) {
          writeln(m[1]);
          writeln(m.hit);
        }
      }
      ST_txtPlusHasFootnotesUrlsImages ret;
      {
        ret.obj_txt                       = obj_txt_out;
        ret.has_notes_reg                 = ftn.has_notes_reg;
        ret.has_notes_star                = ftn.has_notes_star;
        ret.has_notes_plus                = ftn.has_notes_plus;
        ret.has_urls                      = urls;
        ret.has_images_without_dimensions = images_without_dimensions;
      }
      return ret;
    }
    private ST_txtPlusHasFootnotesUrlsImages object_only_()(
      string obj_txt_in,
      bool reset_note_numbers = false
    ) {
      ST_txtPlusHasFootnotesUrlsImages ret;
      {
        ret.obj_txt                       = obj_txt_in;
        ret.has_notes_reg                 = false;
        ret.has_notes_star                = false;
        ret.has_notes_plus                = false;
        ret.has_urls                      = false;
        ret.has_images_without_dimensions = false;
      }
      return ret;
    }
    ST_txtPlusHasFootnotesUrlsImages init() {
      ST_txtPlusHasFootnotesUrlsImages ret = object_notes_and_links_("");
      return ret;
    }
    invariant() {
    }
    ST_txtPlusHasFootnotesUrlsImages munge_heading()(
      string obj_txt_in,
      bool reset_note_numbers = false
    ) {
      obj_txt["munge"] = obj_txt_in
       .replaceFirst(rgx.headings, "")
       .replaceFirst(rgx.object_number_off_all, "")
       .replaceFirst(rgx.markup_inline_linebreak, mkup.br_line_inline)
       .strip;
      ST_txtPlusHasFootnotesUrlsImages ret = object_notes_and_links_(obj_txt["munge"], reset_note_numbers);
      debug(munge) { writeln(__LINE__); writeln(obj_txt_in); writeln(__LINE__); writeln(obj_txt["munge"].to!string); }
      return ret;
    }
    invariant() {
    }
    ST_txtPlusHasFootnotesUrlsImages munge_para()(string obj_txt_in) {
      obj_txt["munge"] = (obj_txt_in)
        .replaceFirst(rgx.para_attribs, "")
        .replaceFirst(rgx.object_number_off_all, "")
        .replaceFirst(rgx.markup_inline_linebreak, mkup.br_line_inline);
      ST_txtPlusHasFootnotesUrlsImages ret = object_notes_and_links_(obj_txt["munge"]);
      debug(munge) { writeln(__LINE__); writeln(obj_txt_in);
        writeln(__LINE__);
        writeln(obj_txt["munge"].to!string);
      }
      return ret;
    }
    ST_txtPlusHasFootnotesUrlsImages munge_quote()(string obj_txt_in) {
      ST_txtPlusHasFootnotesUrlsImages ret = object_notes_and_links_(obj_txt_in.split("\n\n").join(" \\\\\n \\\\\n"));
      return ret;
    }
    invariant() {
    }
    ST_txtPlusHasFootnotesUrlsImages munge_group(string obj_txt_in) {
      ST_txtPlusHasFootnotesUrlsImages ret = object_notes_and_links_(obj_txt_in.split("\n\n").join("\n" ~ mkup.br_line_spaced ~ "\n"));
      return ret;
    }
    invariant() {
    }
    ST_txtPlusHasFootnotesUrlsImages munge_block()(string obj_txt_in) {
      ST_txtPlusHasFootnotesUrlsImages ret = object_notes_and_links_(obj_txt_in);
      return ret;
    }
    invariant() {
    }
    auto munge_verse()(string obj_txt_in) {
      ST_txtPlusHasFootnotesUrlsImages ret = object_notes_and_links_(obj_txt_in);
      return ret;
    }
    invariant() {
    }
    ST_txtPlusHasFootnotesUrlsImages munge_code()(string obj_txt_in) {
      obj_txt_in = obj_txt_in.replaceAll(rgx.space, mkup.nbsp);
      ST_txtPlusHasFootnotesUrlsImages ret = object_only_(obj_txt_in);
      return ret;
    }
    invariant() {
    }
    ST_txtPlusHasFootnotesUrlsImages munge_table()(string obj_txt_in) {
      ST_txtPlusHasFootnotesUrlsImages ret = object_notes_and_links_(obj_txt_in);
      return ret;
    }
    invariant() {
    }
    ST_txtPlusHasFootnotesUrlsImages munge_comment()(string obj_txt_in) {
      ST_txtPlusHasFootnotesUrlsImages ret = object_only_(obj_txt_in);
      return ret;
    }
    invariant() {
    }
  }
  // ↑ - object inline munge
  // ↓ - object inline markup
  static struct ObjInlineMarkup {
    static auto rgx = RgxI();
    static auto munge = ObjInlineMarkupMunge();
    string[string] obj_txt;
    string anchor_tag = "";
    ST_txtAndAnchorTagPlusHasFootnotesUrlsImages obj_inline_markup_and_anchor_tags_and_misc(CMM)(
      string[string]   obj_,
      string           obj_key_,
      CMM              conf_make_meta,
      Flag!"_new_doc"  _new_doc
    ) {
      obj_txt["munge"]                                = obj_[obj_key_].dup;
      obj_txt["munge"]                                = (obj_["is"].match(ctRegex!(`verse|code`)))
      ? obj_txt["munge"]
      : obj_txt["munge"].strip;
      if (_new_doc) {
        anchor_tag = "";
      }
      auto x = munge.init;
      ST_txtAndAnchorTagPlusHasFootnotesUrlsImages ret;
      ret.obj_txt                       = "";
      ret.anchor_tag                    = "";
      ret.has_notes_reg                 = false;
      ret.has_notes_star                = false;
      ret.has_notes_plus                = false;
      ret.has_links                     = false;
      ret.has_images_without_dimensions = false;
      if ((obj_["is"] == "para")
        || (obj_["is"] == "heading")
        || (obj_["is"] == "quote")
        || (obj_["is"] == "group")
        || (obj_["is"] == "block")
        || (obj_["is"] == "verse")) {
        obj_txt["munge"]                              = (obj_txt["munge"]).inline_markup_faces;
        obj_txt["munge"]                              = (obj_txt["munge"]).links_and_images;
      }
      switch (obj_["is"]) {
      case "heading":
        if (_new_doc) {
          anchor_tag                                  = "";
        }
        obj_txt["munge"] = _configured_auto_heading_numbering_and_segment_anchor_tags(obj_txt["munge"], obj_, conf_make_meta, _new_doc);
        obj_txt["munge"] = _make_segment_anchor_tags_if_none_provided(obj_txt["munge"], obj_["lev"], _new_doc);
        if (auto m = obj_txt["munge"].match(rgx.heading_anchor_tag)) {
          anchor_tag                                  = m.captures[1];
        } else if (obj_["lev"] == "1") {
          writeln("heading anchor tag missing: ", obj_txt["munge"]);
        }
        x                                             = munge.munge_heading(obj_txt["munge"], reset_note_numbers);
        reset_note_numbers = false;
        goto default;
      case "para":
        x                                             = munge.munge_para(obj_txt["munge"]);
        goto default;
      case "group":
        x                                             = munge.munge_group(obj_txt["munge"]);
        goto default;
      case "block":
        x                                             = munge.munge_block(obj_txt["munge"]);
        goto default;
      case "quote":
        x                                             = munge.munge_quote(obj_txt["munge"]);
        goto default;
      case "verse":
        x                                             = munge.munge_verse(obj_txt["munge"]);
        goto default;
      case "code":
        x                                             = munge.munge_code(obj_txt["munge"]);
        goto default;
      case "table":
        x                                             = munge.munge_table(obj_txt["munge"]);
        goto default;
      case "comment":
        x                                             = munge.munge_comment(obj_txt["munge"]);
        goto default;
      case "doc_end_reset":
        munge.initialize_note_numbers();
        break;
      default:
        // para, heading, group, block, verse
        ret.obj_txt                       = x.obj_txt;
        ret.anchor_tag                    = anchor_tag;
        ret.has_notes_reg                 = x.has_notes_reg;
        ret.has_notes_star                = x.has_notes_star;
        ret.has_notes_plus                = x.has_notes_plus;
        ret.has_links                     = x.has_urls;
        ret.has_images_without_dimensions = x.has_images_without_dimensions;
        break;
      }
      anchor_tag = "";
      return ret;
    }
    invariant() {
    }
    auto _clean_heading_toc_()(
      char[] heading_toc_,
    ) {
     auto m = (cast(char[]) heading_toc_).matchFirst(rgx.heading);
     heading_toc_ = (m.post).replaceAll(rgx.inline_notes_curly_gen, "");
     return heading_toc_;
    };
    ST_flow_table_of_contents_gather_headings flow_table_of_contents_gather_headings(CMM)( //
      string[string]         obj_,
      CMM                    conf_make_meta,
      string[string]         tag_in_seg,
      string                 _anchor_tag,
      string[][string]       lev4_subtoc,
      ObjGenericComposite[]  the_document_toc_section,
    ) {
      ObjGenericComposite comp_obj_;
      mixin InternalMarkup;
      static auto mkup = InlineMarkup();
      char[] heading_toc_                             = (obj_["substantive"].dup.strip.to!(char[]))
        .replaceAll(rgx.inline_notes_al, "");
      heading_toc_                                    = _clean_heading_toc_(heading_toc_);
      auto attrib = "";
      string toc_txt_, subtoc_txt_;
      int[string] indent;
      if (obj_["lev_markup_number"].to!int > 0) {
        indent = [
          "hang_position" : obj_["lev_markup_number"].to!int,
          "base_position" : obj_["lev_markup_number"].to!int,
        ];
        toc_txt_ = format("%s%s%s%s#%s%s",
          mkup.lnk_o,
          heading_toc_.strip,
          mkup.lnk_c,
          mkup.url_o,
          _anchor_tag,
          mkup.url_c,
        );
        toc_txt_= toc_txt_.links_and_images;
        comp_obj_                                  = set_object_generic("frontmatter", "toc", "para", "toc", toc_txt_.to!string.strip, 0);
        comp_obj_.metainfo.identifier              = "";
        comp_obj_.metainfo.object_number_off       = true;
        comp_obj_.metainfo.object_number_type      = 0;
        comp_obj_.metainfo.dummy_heading           = (an_object["dummy_heading_status"] == "t") ? true: false;
        comp_obj_.attrib.indent_hang               = indent["hang_position"];
        comp_obj_.attrib.indent_base               = indent["base_position"];
        comp_obj_.attrib.bullet                    = false;
        comp_obj_.has.inline_links                 = true;
        the_document_toc_section                   ~= comp_obj_;
      }
      comp_obj_                                    = comp_obj_.init;
      comp_obj_.metainfo.is_of_part                = "frontmatter";
      comp_obj_.metainfo.is_of_section             = "toc";
      comp_obj_.metainfo.is_of_type                = "para";
      comp_obj_.metainfo.is_a                      = "toc";
      comp_obj_.metainfo.ocn                       = 0;
      comp_obj_.metainfo.identifier                = "";
      comp_obj_.metainfo.object_number_off         = true;
      comp_obj_.metainfo.object_number_type        = 0;
      comp_obj_.metainfo.dummy_heading             = (an_object["dummy_heading_status"] == "t") ? true: false;
      comp_obj_.attrib.bullet                      = false;
      comp_obj_.has.inline_links                   = true;
      switch (obj_["lev_markup_number"].to!int) {
      case 0: .. case 3:
        break;
      case 4:
        lev4_subtoc[tag_in_seg["seg_lv4"]] = [];
        break;
      case 5: .. case 7:
        subtoc_txt_ = format("%s%s%s%s#%s%s",
          mkup.lnk_o,
          heading_toc_.strip,
          mkup.lnk_c,
          mkup.url_o,
          _anchor_tag,
          mkup.url_c,
        );
        lev4_subtoc[tag_in_seg["seg_lv4"]]
        ~= links_and_images(obj_["lev_markup_number"]
             ~ "~ " ~ subtoc_txt_.to!string.strip
           );
        break;
      default:
        break;
      }
      ST_flow_table_of_contents_gather_headings ret;
      {
        ret.the_document_toc_section = the_document_toc_section;
        ret.lev4_subtoc              = lev4_subtoc;
      }
      return ret;
    }
    invariant() {
    }
  private:
    static int[] heading_num = [ 0, 0, 0, 0 ];
    static string heading_number_auto_composite = "";
    static string heading_number_auto_composite_segname = "";
    static bool[] auto_heading_numbering = [ true, true, true, true];
    static string _configured_auto_heading_numbering_and_segment_anchor_tags(CMM)(
      string           munge_,
      string[string]   obj_,
      CMM              conf_make_meta,
      bool             _new_doc,
    ) {
      if (_new_doc) {
        heading_num                         = [ 0, 0, 0, 0 ];
        heading_number_auto_composite       = "";
        auto_heading_numbering              = [ true, true, true, true];
      }
      if (conf_make_meta.make.auto_num_top_lv) {
        if (obj_["lev_markup_number"].to!int == 0) {
          heading_num[0]                    = 0;
          heading_num[1]                    = 0;
          heading_num[2]                    = 0;
          heading_num[3]                    = 0;
          heading_number_auto_composite     = "";
        }
        // auto_num_depth minimum 0
        // (1.) default 2 (1.1.1) max 3 (1.1.1.1) implement
        if (
          conf_make_meta.make.auto_num_top_lv
          > obj_["lev_markup_number"].to!uint
        ) {
          heading_num[1]                    = 0;
          heading_num[2]                    = 0;
          heading_num[3]                    = 0;
        } else if (
          conf_make_meta.make.auto_num_top_lv
            == obj_["lev_markup_number"].to!uint
        ) {
          auto_heading_numbering[0] =
            (munge_.match(rgx.auto_heading_numbering_off_lv1)) ? false : true;
          if (auto_heading_numbering[0]) {
            heading_num[0] ++;
          }
          heading_num[1]                    = 0;
          heading_num[2]                    = 0;
          heading_num[3]                    = 0;
        } else if (
          conf_make_meta.make.auto_num_top_lv
            == (obj_["lev_markup_number"].to!uint - 1)
        ) {
          auto_heading_numbering[1] =
            (munge_.match(rgx.auto_heading_numbering_off_lv2)) ? false : true;
          if (auto_heading_numbering[0]
          && auto_heading_numbering[1]) {
            heading_num[1] ++;
          }
          heading_num[2]                    = 0;
          heading_num[3]                    = 0;
        } else if (
          conf_make_meta.make.auto_num_top_lv
            == (obj_["lev_markup_number"].to!uint - 2)
        ) {
          auto_heading_numbering[2] =
            (munge_.match(rgx.auto_heading_numbering_off_lv3)) ? false : true;
          if (auto_heading_numbering[0]
          && auto_heading_numbering[1]
          && auto_heading_numbering[2]) {
            heading_num[2] ++;
          }
          heading_num[3]                    = 0;
        } else if (
          conf_make_meta.make.auto_num_top_lv
            == (obj_["lev_markup_number"].to!uint - 3)
        ) {
          auto_heading_numbering[3] =
            (munge_.match(rgx.auto_heading_numbering_off_lv4)) ? false : true;
          if (auto_heading_numbering[0]
          && auto_heading_numbering[1]
          && auto_heading_numbering[2]
          && auto_heading_numbering[3]) {
            heading_num[3] ++;
          }
        }
        if (auto_heading_numbering[0]) {
          if (heading_num[3] > 0) {
            heading_number_auto_composite
              = (conf_make_meta.make.auto_num_depth.to!uint == 3
                && auto_heading_numbering[3])
              ? (format(q"┃%s.%s.%s.%s┃",
                  heading_num[0].to!string,
                  heading_num[1].to!string,
                  heading_num[2].to!string,
                  heading_num[3].to!string
                ))
              : "";
          } else if (heading_num[2] > 0) {
            heading_number_auto_composite
              = ((conf_make_meta.make.auto_num_depth.to!uint >= 2)
                && (conf_make_meta.make.auto_num_depth.to!uint <= 3)
                && auto_heading_numbering[2])
              ? (format(q"┃%s.%s.%s┃",
                  heading_num[0].to!string,
                  heading_num[1].to!string,
                  heading_num[2].to!string
                ))
              : "";
          } else if (heading_num[1] > 0) {
            heading_number_auto_composite
              = ((conf_make_meta.make.auto_num_depth.to!uint >= 1)
                && (conf_make_meta.make.auto_num_depth.to!uint <= 3)
                && auto_heading_numbering[1])
              ? (format(q"┃%s.%s┃",
                  heading_num[0].to!string,
                  heading_num[1].to!string
                ))
              : "";
          } else if (heading_num[0] > 0
            && munge_.match(rgx.auto_heading_numbering_lv1)
          ) {
            heading_number_auto_composite
              = ((conf_make_meta.make.auto_num_depth.to!uint >= 0)
                && (conf_make_meta.make.auto_num_depth.to!uint <= 3)
                && auto_heading_numbering[0])
              ? (format(q"┃%s┃",
                  heading_num[0].to!string
                ))
              : "";
          } else {
            heading_number_auto_composite = "";
          }
        }
        heading_number_auto_composite_segname =
          (heading_number_auto_composite.empty)
            ? ""
            : "seg_" ~ heading_number_auto_composite;
        debug(heading_number_auto) { writeln(heading_number_auto_composite); }
        if ((!empty(heading_number_auto_composite))
        && (obj_["lev_markup_number"].to!uint >= conf_make_meta.make.auto_num_top_lv)) {
          munge_ = munge_
          .replaceFirst(rgx.heading,
            "$1~$2 " ~ heading_number_auto_composite ~ ". ")
          .replaceFirst(rgx.heading_marker_missing_tag,
            "$1~" ~ heading_number_auto_composite_segname ~ " ");
        }
      }
      return munge_;
    }
    static int heading_num_lev1 = 0;
    static string _make_segment_anchor_tags_if_none_provided()(
      string munge_,
      string lev_,
      bool   _new_doc
    ) {
      if (!(munge_.match(rgx.heading_anchor_tag))) {
        if (lev_ == "A") { // (_new_doc)
          heading_num_lev1 = 0;
        }
        if (munge_.match(rgx.heading_identify_anchor_tag)) {
          if (auto m = munge_.match(rgx.heading_extract_named_anchor_tag)) {
            munge_ = munge_.replaceFirst(
              rgx.heading_marker_missing_tag,
              "$1~" ~ m.captures[1].toLower ~ "_"  ~ m.captures[2] ~ " ");
            if (auto n = munge_.match(rgx.heading_anchor_tag_plus_colon)) {
              auto tag_remunge_ = n.captures[2]
                .replaceAll(rgx.heading_marker_tag_has_colon, "..");
              munge_ = munge_.replaceFirst(rgx.heading_anchor_tag_plus_colon, n.captures[1] ~ tag_remunge_ ~ " ");
            }
          } else if (auto m = munge_.match(rgx.heading_extract_unnamed_anchor_tag)) {
            munge_ = munge_.replaceFirst(
              rgx.heading_marker_missing_tag,
              "$1~" ~ "s" ~ m.captures[1] ~ " ");
          }
        } else if (lev_ == "1") { // (if not successful) manufacture a unique anchor tag for lev == "1"
          heading_num_lev1 ++;
          munge_ = munge_.replaceFirst(
            rgx.heading_marker_missing_tag,
            "$1~" ~ "x" ~ heading_num_lev1.to!string ~ " ");
        }
      }
      return munge_;
    }
  }
  // ↑ - object inline markup
  // ↓ - object attributes
  struct ObjAttributes {
    string[string] _obj_attrib;
    string obj_attributes()(
      string              obj_is_,
      string              obj_raw,
      ObjGenericComposite comp_obj_,
    ) {
      scope(exit) {
        destroy(obj_is_);
        destroy(obj_raw);
        destroy(comp_obj_);
      }
      _obj_attrib["json"] ="{";
      switch (obj_is_) {
      case "heading":
        _obj_attrib["json"] ~= txt_heading(obj_raw);
        break;
      case "para":
        _obj_attrib["json"] ~= txt_para_and_blocks(obj_raw)
        ~ txt_para(obj_raw);
        break;
      case "code":
        _obj_attrib["json"] ~= txt_code(obj_raw);
        break;
      case "group":
        _obj_attrib["json"] ~= txt_para_and_blocks(obj_raw)
        ~ txt_group(obj_raw);
        break;
      case "block":
        _obj_attrib["json"] ~= txt_para_and_blocks(obj_raw)
        ~ txt_block(obj_raw);
        break;
      case "verse":
        _obj_attrib["json"] ~= txt_verse(obj_raw);
        break;
      case "quote":
        _obj_attrib["json"] ~= txt_quote(obj_raw);
        break;
      case "table":
        _obj_attrib["json"] ~= txt_table(obj_raw);
        break;
      case "comment":
        _obj_attrib["json"] ~= txt_comment(obj_raw);
        break;
      default:
        _obj_attrib["json"] ~= txt_para(obj_raw);
        break;
      }
      _obj_attrib["json"] ~= " }";
      _obj_attrib["json"] = _set_additional_values_parse_as_json(_obj_attrib["json"], obj_is_, comp_obj_);
      debug(structattrib) {
        if (oa_j["is"].str() == "heading") {
          writeln(_obj_attrib["json"]);
          writeln(
            "is: ", oa_j["is"].str(),
            "; object_number: ", oa_j["object_number"].integer()
          );
        }
      }
      return _obj_attrib["json"];
    }
    invariant() {
    }
    private:
    string _obj_attributes;
    string txt_para_and_blocks()(string obj_txt_in) {
      if (obj_txt_in.matchFirst(rgx.para_bullet)) {
        _obj_attributes =" \"bullet\": \"true\","
        ~ " \"indent_hang\": 0,"
        ~ " \"indent_base\": 0,";
      } else if (auto m = obj_txt_in.matchFirst(rgx.para_bullet_indent)) {
        _obj_attributes =" \"bullet\": \"true\","
        ~ " \"indent_hang\": " ~ m["indent"].to!string ~ ","
        ~ " \"indent_base\": " ~ m["indent"].to!string ~ ",";
      } else if (auto m = obj_txt_in.matchFirst(rgx.para_indent_hang)) {
        _obj_attributes =" \"bullet\": \"false\","
        ~ " \"indent_hang\": " ~ m["hang"].to!string ~ ","
        ~ " \"indent_base\": " ~ m["indent"].to!string ~ ",";
      } else if (auto m = obj_txt_in.matchFirst(rgx.para_indent)) {
        _obj_attributes =" \"bullet\": \"false\","
        ~ " \"indent_hang\": " ~ m["indent"].to!string ~ ","
        ~ " \"indent_base\": " ~ m["indent"].to!string ~ ",";
      } else {
        _obj_attributes =" \"bullet\": \"false\","
        ~ " \"indent_hang\": 0,"
        ~ " \"indent_base\": 0,";
      }
      return _obj_attributes;
    }
    string txt_heading()(string obj_txt_in) {
      _obj_attributes = " \"use\": \"content\","
      ~ " \"of\": \"para\","
      ~ " \"is\": \"heading\"";
      return _obj_attributes;
    }
    invariant() {
    }
    string txt_para()(string obj_txt_in) {
      _obj_attributes = " \"use\": \"content\","
      ~ " \"of\": \"para\","
      ~ " \"is\": \"para\"";
      return _obj_attributes;
    }
    invariant() {
    }
    string txt_quote()(string obj_txt_in) {
      _obj_attributes = " \"use\": \"content\","
      ~ " \"of\": \"block\","
      ~ " \"is\": \"quote\"";
      return _obj_attributes;
    }
    invariant() {
    }
    string txt_group()(string obj_txt_in) {
      _obj_attributes = " \"use\": \"content\","
      ~ " \"of\": \"block\","
      ~ " \"is\": \"group\"";
      return _obj_attributes;
    }
    invariant() {
    }
    string txt_block()(string obj_txt_in) {
      _obj_attributes = " \"use\": \"content\","
      ~ " \"of\": \"block\","
      ~ " \"is\": \"block\"";
      return _obj_attributes;
    }
    invariant() {
    }
    string txt_verse()(string obj_txt_in) {
      _obj_attributes = " \"use\": \"content\","
      ~ " \"of\": \"block\","
      ~ " \"is\": \"verse\"";
      return _obj_attributes;
    }
    invariant() {
    }
    string txt_code()(string obj_txt_in) {
      _obj_attributes = " \"use\": \"content\","
      ~ " \"of\": \"block\","
      ~ " \"is\": \"code\"";
      return _obj_attributes;
    }
    invariant() {
    }
    string txt_table()(string obj_txt_in) {
      _obj_attributes = " \"use\": \"content\","
      ~ " \"of\": \"block\","
      ~ " \"is\": \"table\"";
      return _obj_attributes;
    }
    invariant() {
    }
    string txt_comment()(string obj_txt_in) {
      _obj_attributes = " \"use\": \"comment\","
      ~ " \"of\": \"comment\","
      ~ " \"is\": \"comment\"";
      return _obj_attributes;
    }
    invariant() {
    }
    string _set_additional_values_parse_as_json()(
      string              _obj_attrib,
      string              obj_is_,
      ObjGenericComposite comp_obj_,
    ) {
      JSONValue oa_j = parseJSON(_obj_attrib);
      assert(
        (oa_j.type == JSON_TYPE.OBJECT)
      );
      if (obj_is_ == "heading") {
        oa_j.object["object_number"]              = comp_obj_.metainfo.ocn;
        oa_j.object["lev_markup_number"]          = comp_obj_.metainfo.heading_lev_markup;
        oa_j.object["lev_collapsed_number"]       = comp_obj_.metainfo.heading_lev_collapsed;
        oa_j.object["heading_ptr"]                = comp_obj_.ptr.heading;
        oa_j.object["doc_object_ptr"]             = comp_obj_.ptr.doc_object;
      }
      oa_j.object["parent_object_number"]         = comp_obj_.metainfo.parent_ocn;
      oa_j.object["parent_lev_markup_number"]     = comp_obj_.metainfo.parent_lev_markup;
      _obj_attrib                                 = oa_j.toString();
      return _obj_attrib;
    }
  }
  // ↑ - object attributes
  // ↓ - object tags
  pure ObjGenericComposite obj_dom_structure_set_markup_tags()(
    ObjGenericComposite  obj,
    int[]                dom,
    int                  lev
  ) {
    foreach (i; 0 .. 8) {
      if (i < lev) {
        if (dom[i] == DomTags.open
           || dom[i] == DomTags.close_and_open
        ) {
          dom[i] = DomTags.open_still;
        } else if (dom[i] == DomTags.close) {
          dom[i] = DomTags.none;
        }
      } else if (i == lev) {
        if (lev  == 0
          && dom[i] == DomTags.open_still
        ) {
          dom[i] = DomTags.close;
        } else if (dom[i] == DomTags.open
          || dom[i] == DomTags.open_still
          || dom[i] == DomTags.close_and_open
        ) {
          dom[i] = DomTags.close_and_open;
        } else {
          dom[i] = DomTags.open;
        }
      } else if (i > lev) {
        if (dom[i] == DomTags.close) {
          dom[i] = DomTags.none;
        } else if (dom[i] == DomTags.open
          || dom[i] == DomTags.open_still
          || dom[i] == DomTags.close_and_open
        ) {
          dom[i] = DomTags.close;
        }
      }
    }
    debug(dom_magic_numbers) { writeln("marked up: ", lev, ": ", dom); }
    obj.metainfo.dom_structure_markedup_tags_status = dom.dup;
    return obj;
  }
  pure ObjGenericComposite obj_dom_set_collapsed_tags()(
    ObjGenericComposite  obj,
    int[]                dom,
    int                  lev
  ) {
    foreach (i; 0 .. 8) {
      if (i < lev) {
        if (dom[i] == DomTags.open
           || dom[i] == DomTags.close_and_open
        ) {
          dom[i] = DomTags.open_still;
        } else if (dom[i] == DomTags.close) {
          dom[i] = DomTags.none;
        }
      } else if (i == lev) {
        if (lev  == 0
          && dom[i] == DomTags.open_still
        ) {
          dom[i] = DomTags.close;
        } else if (dom[i] == DomTags.open
          || dom[i] == DomTags.open_still
          || dom[i] == DomTags.close_and_open
        ) {
          dom[i] = DomTags.close_and_open;
        } else {
          dom[i] = DomTags.open;
        }
      } else if (i > lev) {
        if (dom[i] == DomTags.close) {
          dom[i] = DomTags.none;
        } else if (dom[i] == DomTags.open
          || dom[i] == DomTags.open_still
          || dom[i] == DomTags.close_and_open
        ) {
          dom[i] = DomTags.close;
        }
      }
    }
    debug(dom_magic_numbers) { writeln("collapsed: ", lev, ": ", dom); }
    obj.metainfo.dom_structure_collapsed_tags_status = dom.dup;
    return obj;
  }
  // ↑ - object tags
  // ↓ - object digest
  pure ubyte[32] obj_digest()(
    ObjGenericComposite  obj,
  ) {
    obj.metainfo.sha256 = obj.text.sha256Of;
    return obj.metainfo.sha256;
  }
  // ↑ - object digest
  // ↓ - table of contents
  @system ObjGenericComposite[] backmatter_gather_table_of_contents(
    ObjGenericComposite[] the_document_endnotes_section,
    ObjGenericComposite[] the_document_glossary_section,
    ObjGenericComposite[] the_document_bibliography_section,
    ObjGenericComposite[] the_document_bookindex_section,
    ObjGenericComposite[] the_document_blurb_section,
  ) {
    ObjGenericComposite[] toc_section_backmatter;
    string toc_txt_;
    static auto mkup = InlineMarkup();
    ObjGenericComposite   comp_obj_;
    int[string] indent = [
      "hang_position" : 1,
      "base_position" : 1,
    ];
    comp_obj_                                          = set_object_generic("frontmatter", "toc", "para", "toc", "", 0);
    comp_obj_.metainfo.identifier                      = "";
    comp_obj_.metainfo.object_number_off               = true;
    comp_obj_.metainfo.object_number_type              = 0;
    comp_obj_.attrib.indent_hang                       = indent["hang_position"];
    comp_obj_.attrib.indent_base                       = indent["base_position"];
    comp_obj_.attrib.bullet                            = false;
    if (the_document_endnotes_section.length > 1) {
      toc_txt_ = format("%s%s%s%s#%s%s",
        mkup.lnk_o,
        "Endnotes",
        mkup.lnk_c,
        mkup.url_o,
        "endnotes",
        mkup.url_c,
      );
      toc_txt_= toc_txt_.links_and_images;
      comp_obj_.text                                   = toc_txt_.to!string.strip;
      comp_obj_.has.inline_links                       = true;
      toc_section_backmatter                           ~= comp_obj_;
    }
    if (the_document_glossary_section.length > 1) {
      toc_txt_ = format("%s%s%s%s#%s%s",
        mkup.lnk_o,
        "Glossary",
        mkup.lnk_c,
        mkup.url_o,
        "glossary",
        mkup.url_c,
      );
      toc_txt_= toc_txt_.links_and_images;
      comp_obj_.text                                   = toc_txt_.to!string.strip;
      comp_obj_.has.inline_links                       = true;
      toc_section_backmatter                           ~= comp_obj_;
    }
    if (the_document_bibliography_section.length > 1){
      toc_txt_ = format("%s%s%s%s#%s%s",
        mkup.lnk_o,
        "Bibliography",
        mkup.lnk_c,
        mkup.url_o,
        "bibliography",
        mkup.url_c,
      );
      toc_txt_= toc_txt_.links_and_images;
      comp_obj_.text                                   = toc_txt_.to!string.strip;
      comp_obj_.has.inline_links                       = true;
      toc_section_backmatter                           ~= comp_obj_;
    }
    if (the_document_bookindex_section.length > 1) {
      toc_txt_ = format("%s%s%s%s#%s%s",
        mkup.lnk_o,
        "Book Index",
        mkup.lnk_c,
        mkup.url_o,
        "bookindex",
        mkup.url_c,
      );
      toc_txt_= toc_txt_.links_and_images;
      comp_obj_.text                                   = toc_txt_.to!string.strip;
      comp_obj_.has.inline_links                       = true;
      toc_section_backmatter                           ~= comp_obj_;
    }
    if (the_document_blurb_section.length > 1) {
      toc_txt_ = format("%s%s%s%s#%s%s",
        mkup.lnk_o,
        "Blurb",
        mkup.lnk_c,
        mkup.url_o,
        "blurb",
        mkup.url_c,
      );
      toc_txt_= toc_txt_.links_and_images;
      comp_obj_.has.inline_links                       = true;
      comp_obj_.text                                   = toc_txt_.to!string.strip;
      toc_section_backmatter                           ~= comp_obj_;
    }
    debug(toc) {
      writefln( "%s %s", __LINE__,);
      foreach (toc_linked_heading; toc_section_backmatter) {
        writeln(mkup.indent_by_spaces_provided(toc_linked_heading.attrib.indent_hang), toc_linked_heading.text);
      }
    }
    return toc_section_backmatter;
  }
  // ↑ - table of contents
  // ↓ - endnotes
  struct NotesSection {
    string[string] object_notes;
    int previous_count;
    int mkn;
    static auto rgx = RgxI();
    private auto gather_notes_for_endnote_section(
      ObjGenericComposite[] contents_am,
      string[string]        tag_in_seg,
      int                   cntr,
    ) {
      assert((contents_am[cntr].metainfo.is_a == "para")
      || (contents_am[cntr].metainfo.is_a     == "heading")
      || (contents_am[cntr].metainfo.is_a     == "quote")
      || (contents_am[cntr].metainfo.is_a     == "group")
      || (contents_am[cntr].metainfo.is_a     == "block")
      || (contents_am[cntr].metainfo.is_a     == "verse"));
      assert(cntr >= previous_count);
      assert(
        (contents_am[cntr].text).match(
        rgx.inline_notes_al_all_note)
      );
      mixin InternalMarkup;
      previous_count = cntr;
      static auto mkup = InlineMarkup();
      static auto munge = ObjInlineMarkupMunge();
      foreach(m;
        (contents_am[cntr].text).matchAll(
          rgx.inline_notes_al_special_char_note)
      ) {
        debug(endnotes_build) { writeln(
            "{", mkup.ff_i, mkup.superscript, mkup.ff_o, m["char"], ".", mkup.ff_c, mkup.superscript, "}"
            ~ mkup.mark_internal_site_lnk,
            tag_in_seg["seg_lv4"],
              ".fnSuffix#noteref_\n  ", m["char"], " ",
            m["note"]); // sometimes need segment name (segmented html & epub)
        }
        // you need anchor for segments at this point ->
        object_notes["anchor"] ~= "note_" ~ m["char"] ~ "』";
        object_notes["notes"]  ~= (tag_in_seg["seg_lv4"].empty)
        ? (links_and_images(
            "{" ~ mkup.ff_i ~ mkup.superscript  ~ mkup.ff_o ~ m["char"] ~ "." ~ mkup.ff_c  ~ mkup.superscript  ~ "}#noteref_"
            ~ m["char"]) ~ " "
            ~ m["note"] ~ "』"
          )
        : (links_and_images(
            "{" ~ mkup.ff_i ~ mkup.superscript ~ mkup.ff_o ~ m["char"] ~ "." ~ mkup.ff_c  ~ mkup.superscript ~ "}"
             ~ mkup.mark_internal_site_lnk
             ~ tag_in_seg["seg_lv4"]
             ~ ".fnSuffix#noteref_"
             ~ m["char"]) ~ " "
             ~ m["note"] ~ "』"
          );
      }
      foreach(m;
        (contents_am[cntr].text).matchAll(
          rgx.inline_notes_al_regular_number_note)
      ) {
        debug(endnotes_build) { writeln(
            "{", mkup.ff_i, mkup.superscipt, mkup.ff_o, m["num"], ".", mkup.ff_c, mkup.superscipt, "}"
            ~ mkup.mark_internal_site_lnk,
            tag_in_seg["seg_lv4"],
              ".fnSuffix#noteref_\n  ", m["num"], " ",
            m["note"]); // sometimes need segment name (segmented html & epub)
        }
        // you need anchor for segments at this point ->
        object_notes["anchor"] ~= "note_" ~ m["num"] ~ "』";
        object_notes["notes"]  ~= (tag_in_seg["seg_lv4"].empty)
        ? (links_and_images(
            "{" ~ mkup.ff_i ~ mkup.superscript  ~ mkup.ff_o ~ m["num"] ~ "." ~ mkup.ff_c  ~ mkup.superscript  ~ "}#noteref_"
            ~ m["num"]) ~ " "
            ~ m["note"] ~ "』"
          )
        : (links_and_images(
            "{" ~ mkup.ff_i ~ mkup.superscript ~ mkup.ff_o ~ m["num"] ~ "." ~ mkup.ff_c  ~ mkup.superscript ~ "}"
             ~ mkup.mark_internal_site_lnk
             ~ tag_in_seg["seg_lv4"]
             ~ ".fnSuffix#noteref_"
             ~ m["num"]) ~ " "
             ~ m["note"] ~ "』"
          );
      }
      return object_notes;
    }
    private auto gathered_notes() {
      string[][string] endnotes_;
      if (object_notes.length > 1) {
        endnotes_["notes"] = (object_notes["notes"].split(rgx.break_string))[0..$-1];
        endnotes_["anchor"] = (object_notes["anchor"].split(rgx.break_string))[0..$-1];
      } else {
        endnotes_["notes"] = [];
        endnotes_["anchor"] = [];
      }
      return endnotes_;
    }
    private ST_endnotes backmatter_endnote_objects(O)(
      OCNset         obj_cite_digits,
      O              opt_action,
    ) {
      mixin spineNode;
      ObjGenericComposite[] the_document_endnotes_section;
      auto endnotes_ = gathered_notes();
      string type_is;
      string lev, lev_markup_number, lev_collapsed_number;
      string attrib;
      int[string] indent;
      ObjGenericComposite comp_obj_;
      if ((endnotes_["notes"].length > 0)
      && (opt_action.backmatter && opt_action.section_endnotes)) {
        {
          comp_obj_                                     = set_object_heading("lev1", "backmatter", "endnotes", "Endnotes");
          comp_obj_.metainfo.identifier                 = "";
          comp_obj_.metainfo.dummy_heading              = false;
          comp_obj_.metainfo.object_number_off          = false;
          comp_obj_.metainfo.object_number_type         = 0;
          comp_obj_.tags.segment_anchor_tag_epub        = "_part_endnotes";
          comp_obj_.tags.anchor_tag_html                = comp_obj_.tags.segment_anchor_tag_epub;
          comp_obj_.tags.in_segment_html                = "endnotes";
          comp_obj_.tags.anchor_tags                    = ["section_endnotes"];
          the_document_endnotes_section                 ~= comp_obj_;
          tag_assoc[comp_obj_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_.tags.in_segment_html;
          tag_assoc[comp_obj_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_.tags.segment_anchor_tag_epub;
          ++mkn;
        }
        {
          comp_obj_                                     = set_object_heading("lev4", "backmatter", "endnotes", "Endnotes");
          comp_obj_.metainfo.identifier                 = "";
          comp_obj_.metainfo.dummy_heading              = true;
          comp_obj_.metainfo.object_number_off          = true;
          comp_obj_.metainfo.object_number_type         = 0;
          comp_obj_.tags.segment_anchor_tag_epub        = "endnotes";
          comp_obj_.tags.anchor_tag_html                = comp_obj_.tags.segment_anchor_tag_epub;
          comp_obj_.tags.in_segment_html                = comp_obj_.tags.anchor_tag_html;
          comp_obj_.tags.anchor_tags                    = ["endnotes"];
          the_document_endnotes_section                 ~= comp_obj_;
          tag_assoc[comp_obj_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_.tags.in_segment_html;
          tag_assoc[comp_obj_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_.tags.segment_anchor_tag_epub;
          ++mkn;
        }
      } else {
        comp_obj_                                       = set_object_heading("lev1", "empty", "empty", "(skip) there are no Endnotes");
        comp_obj_.metainfo.identifier                   = "";
        comp_obj_.metainfo.dummy_heading                = true;
        comp_obj_.metainfo.object_number_off            = true;
        comp_obj_.metainfo.object_number_type           = 0;
        the_document_endnotes_section                   ~= comp_obj_;
      }
      if (opt_action.backmatter && opt_action.section_endnotes) {
        ObjGenericComposite comp_obj_endnote_;
        comp_obj_endnote_                                       = set_object_generic("backmatter", "endnotes", "para", "endnote", "", 0);
        comp_obj_endnote_.metainfo.identifier                   = "";
        // comp_obj_.metainfo.dummy_heading                     = false;
        comp_obj_.metainfo.object_number_off                    = true;
        comp_obj_.metainfo.object_number_type                   = 0;
        comp_obj_endnote_.attrib.indent_hang                    = 0;
        comp_obj_endnote_.attrib.indent_base                    = 0;
        comp_obj_endnote_.attrib.bullet                         = false;
        foreach (i, endnote; endnotes_["notes"]) {
          auto     m                                            = endnote.matchFirst(rgx.note_ref);
          string   notenumber                                   = m["ref"].to!string;
          string   anchor_tag                                   = "note_" ~ notenumber;
          comp_obj_endnote_.tags.anchor_tags                    = [ endnotes_["anchor"][i] ];
          comp_obj_endnote_.has.inline_links                    = true;
          comp_obj_endnote_.text                                = endnote.inline_markup_faces.strip;
          the_document_endnotes_section                         ~= comp_obj_endnote_;
        }
      }
      ST_endnotes ret;
      {
        ret.endnotes = the_document_endnotes_section;
        ret.ocn      = obj_cite_digits;
      }
      return ret;
    }
  }
  // ↑ - endnotes
  // ↓ - section book index
  @system ST_flow_book_index flow_book_index_(B)(
    char[]          line,
    string[string]  an_object,
    string          book_idx_tmp,
    uint[string]    pith,
    B               opt_action,
  ) {
    static auto rgx = RgxI();
    if (auto m = line.match(rgx.book_index_item)) {                                   // match book_index
      debug(bookindexmatch) { writefln(
          "* [bookindex] %s\n",
          m["bookindex"].to!string,
        );
      }
      an_object["bookindex_nugget"] = m.captures[1].to!string;
    } else if (auto m = line.match(rgx.book_index_item_open))  {                      // match open book_index
      pith["section"] = eN.sect.book_index;
      if (opt_action.backmatter && opt_action.section_bookindex) {
        book_idx_tmp = m.captures[1].to!string;
        debug(bookindexmatch) { writefln( "* [bookindex] %s\n", book_idx_tmp,); }
      }
    } else if (pith["section"] == eN.sect.book_index)  {                    // book_index flag set
      if (auto m = line.match(rgx.book_index_item_close))  {
        pith["section"] = eN.sect.unset;
        if (opt_action.backmatter
        && opt_action.section_bookindex) {
          an_object["bookindex_nugget"] = book_idx_tmp ~ m.captures[1].to!string;
          debug(bookindexmatch) { writefln( "* [bookindex] %s\n", book_idx_tmp,); }
        }
        book_idx_tmp = "";
      } else {
        if (opt_action.backmatter
        && opt_action.section_bookindex) {
          book_idx_tmp ~= line;
        }
      }
    }
    ST_flow_book_index ret;
    {
      ret.this_object    = an_object;
      ret.pith           = pith;
      ret.book_idx_tmp   = book_idx_tmp;
    }
    return ret;
  }
  struct BookIndexNuggetHash {
    string main_term, sub_term, sub_term_bits;
    int object_number_offset, object_number_endpoint;
    string[] object_numbers;
    string[][string][string] bi_hash_nugget;
    string[] bi_main_terms_split_arr;
    string[][string][string] bookindex_nugget_hash(S)(
      string bookindex_section,
      OCNset obj_cite_digits,
      S      tag_in_seg,
    ) {
      debug(asserts) { static assert(is(typeof(obj_cite_digits.object_number) == int)); }
      debug(bookindexraw) {
        if (!bookindex_section.empty) {
          writeln(
            "* [bookindex] ",
            "[", obj_cite_digits.object_number.to!string, ": ", tag_in_seg["seg_lv4"], "] ", bookindex_section,
            "  - - - ",
            "[", obj_cite_digits.object_number.to!string, "] ", bookindex_section
          );
        }
      }
      static auto rgx = RgxI();
      if (!bookindex_section.empty) {
        auto bi_main_terms_split_arr
          = bookindex_section.split(rgx.bi_main_terms_split);
        foreach (bi_main_terms_content; bi_main_terms_split_arr) {
          auto bi_main_term_and_rest
            = bi_main_terms_content.split(rgx.bi_main_term_plus_rest_split);
          if (auto m = bi_main_term_and_rest[0].match(
            rgx.bi_term_and_object_numbers_match)
          ) {
            main_term = m.captures[1].strip;
            object_number_offset = m.captures[2].to!int;
            object_number_endpoint = (obj_cite_digits.object_number + object_number_offset);
            object_numbers ~= (obj_cite_digits.object_number.to!string
            ~ "-" ~ object_number_endpoint.to!string);
          } else {
            main_term = bi_main_term_and_rest[0].strip;
            object_numbers ~= obj_cite_digits.object_number.to!string;
          }
          bi_hash_nugget[main_term]["_a"] ~= object_numbers;
          object_numbers = null;
          if (bi_main_term_and_rest.length > 1) {
            auto bi_sub_terms_split_arr
              = bi_main_term_and_rest[1].split(
                rgx.bi_sub_terms_plus_object_number_offset_split
              );
            foreach (sub_terms_bits; bi_sub_terms_split_arr) {
              if (auto m = sub_terms_bits.match(rgx.bi_term_and_object_numbers_match)) {
                sub_term = m.captures[1].strip;
                object_number_offset = m.captures[2].to!int;
                object_number_endpoint = (obj_cite_digits.object_number + object_number_offset);
                object_numbers ~= (obj_cite_digits.object_number.to!string
                ~ " - " ~ object_number_endpoint.to!string);
              } else {
                sub_term = sub_terms_bits.strip;
                object_numbers ~= obj_cite_digits.object_number.to!string;
              }
              if (!empty(sub_term)) {
                bi_hash_nugget[main_term][sub_term] ~= object_numbers;
              }
              object_numbers = null;
            }
          }
        }
      }
      return bi_hash_nugget;
    }
    invariant() {
    }
  }
  struct BookIndexReportIndent {
    int mkn, skn;
    void bookindex_report_indented()(
      string[][string][string] bookindex_unordered_hashes
    ) {
      auto mainkeys
        = bookindex_unordered_hashes.byKey.array.sort().release;
      foreach (mainkey; mainkeys) {
        debug(bookindex1) { writeln(mainkey); }
        auto subkeys
          = bookindex_unordered_hashes[mainkey].byKey.array.sort().release;
        foreach (subkey; subkeys) {
          debug(bookindex1) {
            writeln("  ", subkey);
            writeln("    ", to!string(
              bookindex_unordered_hashes[mainkey][subkey]
            ));
          }
          ++skn;
        }
        ++mkn;
      }
    }
  }
  struct BookIndexReportSection {
    int  mkn, skn;
    static auto rgx = RgxI();
    static auto munge = ObjInlineMarkupMunge();
    void bookindex_write_section()(
      string[][string][string] bookindex_unordered_hashes
    ) {
      auto mainkeys =
        bookindex_unordered_hashes.byKey.array
        .sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable).release;
      foreach (mainkey; mainkeys) {
        write("_0_1 ⑆!┨", mainkey, "┣! ");
        foreach (ref_; bookindex_unordered_hashes[mainkey]["_a"]) {
          auto go = ref_.replaceAll(rgx.book_index_go, "$1");
          write(" {", ref_, "}#", go, ", ");
        }
        writeln(" \\\\");
        bookindex_unordered_hashes[mainkey].remove("_a");
        auto subkeys =
          bookindex_unordered_hashes[mainkey].byKey.array
          .sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable).release;
        foreach (subkey; subkeys) {
          write("  ", subkey, ", ");
          foreach (ref_; bookindex_unordered_hashes[mainkey][subkey]) {
            auto go = ref_.replaceAll(rgx.book_index_go, "$1");
            write(" {", ref_, "}#", go, ", ");
          }
          writeln(" \\\\");
          ++skn;
        }
        ++mkn;
      }
    }
    @system ST_bookindex backmatter_bookindex_build_abstraction_section(B)(
      string[][string][string] bookindex_unordered_hashes,
      OCNset                   obj_cite_digits,
      B                        opt_action,
    ) {
      debug(asserts) { static assert(is(typeof(obj_cite_digits.object_number) == int)); }
      mixin spineNode;
      mixin InternalMarkup;
      static auto mkup = InlineMarkup();
      string type_is;
      string lev;
      int heading_lev_markup, heading_lev_collapsed;
      string attrib;
      int[string] indent;
      auto mainkeys =
        bookindex_unordered_hashes.byKey.array
        .sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable).release;
      ObjGenericComposite[] bookindex_section;
      ObjGenericComposite comp_obj_;
      auto node_para_int_ = node_metadata_para_int;
      auto node_para_str_ = node_metadata_para_str;
      if ((mainkeys.length > 0)
      && (opt_action.backmatter
      && opt_action.section_bookindex)) {
        string bi_tmp;
        string[] bi_tmp_tags;
        {
          comp_obj_                                     =  set_object_heading("lev1", "backmatter", "bookindex", "Book Index");
          comp_obj_.metainfo.identifier                 = "";
          comp_obj_.metainfo.dummy_heading              = false;
          comp_obj_.metainfo.object_number_off          = false;
          comp_obj_.metainfo.object_number_type         = 0;
          comp_obj_.tags.segment_anchor_tag_epub        = "_part_book_index";
          comp_obj_.tags.anchor_tag_html                = comp_obj_.tags.segment_anchor_tag_epub;
          comp_obj_.tags.in_segment_html                = "bookindex";
          comp_obj_.tags.anchor_tags                    = ["section_bookindex"];
          comp_obj_.has.inline_links                    = true;
          bookindex_section                             ~= comp_obj_;
          tag_assoc[comp_obj_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_.tags.in_segment_html;
          tag_assoc[comp_obj_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_.tags.segment_anchor_tag_epub;
          ++mkn;
        }
        {
          comp_obj_                                     = set_object_heading("lev4", "backmatter", "bookindex", "Index");
          comp_obj_.metainfo.identifier                 = "";
          comp_obj_.metainfo.dummy_heading              = true;
          comp_obj_.metainfo.object_number_off          = true;
          comp_obj_.metainfo.object_number_type         = 0;
          comp_obj_.tags.segment_anchor_tag_epub        = "bookindex";
          comp_obj_.tags.anchor_tag_html                = comp_obj_.tags.segment_anchor_tag_epub;
          comp_obj_.tags.in_segment_html                = comp_obj_.tags.anchor_tag_html;
          comp_obj_.metainfo.heading_lev_collapsed      = 2;
          comp_obj_.has.inline_links                    = false;
          comp_obj_.tags.anchor_tags                    = ["bookindex"];
          bookindex_section                             ~= comp_obj_;
          tag_assoc[comp_obj_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_.tags.in_segment_html;
          tag_assoc[comp_obj_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_.tags.segment_anchor_tag_epub;
          ++mkn;
        }
        import std.array : appender;
        auto buffer = appender!(char[])();
        string[dchar] transTable = [' ' : "_"];
        foreach (mainkey; mainkeys) {
          bi_tmp_tags = [""];
          bi_tmp = mkup.ff_i ~ mkup.bold ~ mkup.ff_o ~ mainkey ~ mkup.ff_c ~ mkup.bold ~ " ";
          buffer.clear();
          bi_tmp_tags ~= translate(mainkey, transTable);
          auto bkidx_lnk(string locs) {
            string markup = "";
            if (auto m = locs.matchFirst(rgx.book_index_go)) {
              markup
                = links_and_images("{ " ~ m["link"] ~ " }"
                ~ "#" ~ m["ocn"] ~ ", ");
            } else {
              writeln(__LINE__, ": ", locs);
            }
            return markup;
          }
          foreach (ref_; bookindex_unordered_hashes[mainkey]["_a"]) {
            bi_tmp ~= bkidx_lnk(ref_);
          }
          bi_tmp ~= " \\\\\n    ";
          bookindex_unordered_hashes[mainkey].remove("_a");
          auto subkeys =
            bookindex_unordered_hashes[mainkey].byKey.array
            .sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable).release;
          foreach (subkey; subkeys) {
            bi_tmp ~= subkey ~ ", ";
            buffer.clear();
            bi_tmp_tags ~= translate(subkey, transTable);
            foreach (ref_; bookindex_unordered_hashes[mainkey][subkey]) {
              bi_tmp ~= bkidx_lnk(ref_);
            }
            bi_tmp ~= " \\\\\n    ";
            ++skn;
          }
          bi_tmp                                             = bi_tmp.replaceFirst(rgx.trailing_linebreak, "");
          comp_obj_                                          = set_object_generic("backmatter", "bookindex", "para", "bookindex", bi_tmp.to!string.strip, 0);
          comp_obj_.metainfo.identifier                      = "";
          comp_obj_.metainfo.object_number_off               = true;
          comp_obj_.metainfo.object_number_type              = 0;
          comp_obj_.tags.anchor_tags                         = bi_tmp_tags;
          comp_obj_.attrib.indent_hang                       = 0;
          comp_obj_.attrib.indent_base                       = 1;
          comp_obj_.attrib.bullet                            = false;
          comp_obj_.has.inline_links                         = true;
          comp_obj_.text                                     = bi_tmp.to!string.strip;
          bookindex_section                                  ~= comp_obj_;
          ++mkn;
        }
      } else {                              // no book index, (figure out what to do here)
        comp_obj_                                       = set_object_heading("lev1", "backmatter", "bookindex", "(skip) there is no Book Index");
        comp_obj_.metainfo.identifier                   = "";
        comp_obj_.metainfo.dummy_heading                = true;
        comp_obj_.metainfo.object_number_off            = true;
        bookindex_section                               ~= comp_obj_;
      }
      ST_bookindex ret;
      {
        ret.bookindex = bookindex_section;
        ret.ocn       = obj_cite_digits;
      }
      return ret;
    }
  }
  // ↑ - section book index
  // ↓ - section glossary
  // ↓ build
  ST_the_section build_the_glossary_section(
    char[]                 line,             // line is immutable, not necessary to return unchanged
    uint[string]           pith,             // double check, should not be necessary to pass pith
    string[string][string] tag_assoc,        // only for headings: html & epub
  ) {
    static auto rgx = RgxI();
    ObjGenericComposite comp_obj_;
    ObjGenericComposite[] add_to_current_document_section;
    indent = [
      "hang_position" : 0,
      "base_position" : 0,
    ];
    bullet = false;
    pith["txt_is"]           = eN.txt_is.para;
    line_occur["para"]       = eN.bi.off;
    an_object_key            = "glossary_nugget";
    ST_the_section ret;
    if (line.matchFirst(rgx.heading_glossary)) {
      {
        comp_obj_                                = set_object_heading("lev1", "backmatter", "glossary", "Glossary");
        comp_obj_.metainfo.identifier            = "";
        comp_obj_.metainfo.dummy_heading         = false;
        comp_obj_.metainfo.object_number_off     = false;
        comp_obj_.metainfo.object_number_type    = 0;
        comp_obj_.tags.segment_anchor_tag_epub   = "_part_glossary";
        comp_obj_.tags.anchor_tag_html           = comp_obj_.tags.segment_anchor_tag_epub;
        comp_obj_.tags.in_segment_html           = "glossary";
        comp_obj_.tags.anchor_tags               = ["section_glossary"];
        comp_obj_.metainfo.dom_structure_markedup_tags_status  = [ 1, 1, 0, 0, 0, 0, 0, 0];
        comp_obj_.metainfo.dom_structure_collapsed_tags_status = [ 1, 1, 0, 0, 0, 0, 0, 0];
        add_to_current_document_section           ~= comp_obj_; //
        tag_assoc[comp_obj_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_.tags.in_segment_html;
        tag_assoc[comp_obj_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_.tags.segment_anchor_tag_epub;
      } {
        comp_obj_                                = set_object_heading("lev4", "backmatter", "glossary", "Glossary");
        comp_obj_.metainfo.identifier            = "";
        comp_obj_.metainfo.dummy_heading         = true;
        comp_obj_.metainfo.object_number_off     = true;
        comp_obj_.metainfo.object_number_type    = 0;
        comp_obj_.tags.segment_anchor_tag_epub   = "glossary";
        comp_obj_.tags.anchor_tag_html           = comp_obj_.tags.segment_anchor_tag_epub;
        comp_obj_.tags.in_segment_html           = comp_obj_.tags.anchor_tag_html;
        comp_obj_.metainfo.heading_lev_collapsed = 2;
        comp_obj_.tags.anchor_tags               = ["glossary"];
        add_to_current_document_section          ~= comp_obj_; //
        tag_assoc[comp_obj_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_.tags.in_segment_html;
        tag_assoc[comp_obj_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_.tags.segment_anchor_tag_epub;
        pith["ocn"] = eN.ocn.on;
      }
      {
        ret.comp_section_obj         ~= add_to_current_document_section;
        ret.pith                     = pith;
        ret.tag_assoc                = tag_assoc; // only for headings: html & epub
      }
    } else { // para
      {
        ST_flow_para_match _get = line.flow_para_match_(an_object, an_object_key, indent, bullet, pith, line_occur);
        {
          an_object     = _get.this_object;
          an_object_key = _get.this_object_key;
          pith          = _get.pith;
          indent        = _get.indent;
          bullet        = _get.bullet;
          line_occur    = _get.line_occur;
        }
      }
      comp_obj_                               = set_object_generic("backmatter", "glossary", "para", "glossary", links_and_images(line.to!string.strip).replaceFirst(rgx.para_attribs, ""), 0);
      comp_obj_.metainfo.identifier           = "";
      comp_obj_.metainfo.object_number_off    = true;
      comp_obj_.metainfo.object_number_type   = 0;
      comp_obj_.attrib.indent_hang            = indent["hang_position"];
      comp_obj_.attrib.indent_base            = indent["base_position"];
      comp_obj_.attrib.bullet                 = bullet;
      add_to_current_document_section         ~= comp_obj_; //
      pith["ocn"]                             = eN.ocn.on;
      {
        ret.comp_section_obj = add_to_current_document_section;
        ret.pith             = pith;
        ret.tag_assoc        = tag_assoc; // NO CHANGE here, only for headings: html & epub
      }
    }
    return ret;
  }
  // ↑ - section glossary
  // ↓ - section bibliography
  @system ST_biblio_section backmatter_make_the_bibliography_section()(
    string[]     biblio_unsorted_incomplete,
    JSONValue[]  bib_arr_json,
  ) {
    Bibliography biblio = Bibliography();
    ObjGenericComposite comp_obj_;
    static auto mkup = InlineMarkup();
    ST_flow_bibliography _get = biblio.flow_bibliography_(biblio_unsorted_incomplete, bib_arr_json);
    JSONValue[] biblio_ordered;
    biblio_ordered            = _get.biblio_sorted;
    if (biblio_ordered.length > 0) {
      {
        comp_obj_                                 = set_object_heading("lev1", "backmatter", "bibliography", "Bibliography");
        comp_obj_.metainfo.identifier             = "";
        comp_obj_.metainfo.dummy_heading          = false;
        comp_obj_.metainfo.object_number_off      = false;
        comp_obj_.metainfo.object_number_type     = 0;
        comp_obj_.tags.segment_anchor_tag_epub    = "_part_bibliography";
        comp_obj_.tags.anchor_tag_html            = comp_obj_.tags.segment_anchor_tag_epub;
        comp_obj_.tags.in_segment_html            = "bibliography";
        comp_obj_.tags.anchor_tags                = ["section_bibliography"];
        comp_obj_.metainfo.dom_structure_markedup_tags_status  = [ 1, 1, 0, 0, 0, 0, 0, 0];
        comp_obj_.metainfo.dom_structure_collapsed_tags_status = [ 1, 1, 0, 0, 0, 0, 0, 0];
        the_document_bibliography_section         ~= comp_obj_;
        tag_assoc[comp_obj_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_.tags.in_segment_html;
        tag_assoc[comp_obj_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_.tags.segment_anchor_tag_epub;
      }
      {
        comp_obj_                                 = set_object_heading("lev4", "backmatter", "bibliography", "Bibliography");
        comp_obj_.metainfo.identifier             = "";
        comp_obj_.metainfo.dummy_heading          = true;
        comp_obj_.metainfo.object_number_off      = true;
        comp_obj_.metainfo.object_number_type     = 0;
        comp_obj_.tags.segment_anchor_tag_epub    = "bibliography";
        comp_obj_.tags.anchor_tag_html            = comp_obj_.tags.segment_anchor_tag_epub;
        comp_obj_.tags.in_segment_html            = comp_obj_.tags.anchor_tag_html;
        comp_obj_.tags.anchor_tags                = ["bibliography"];
        the_document_bibliography_section         ~= comp_obj_;
        tag_assoc[comp_obj_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_.tags.in_segment_html;
        tag_assoc[comp_obj_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_.tags.segment_anchor_tag_epub;
      }
      {
        string out_;
        foreach (entry; biblio_ordered) {
          out_ = format("%s \"%s\"%s%s%s%s%s%s%s%s%s.",
            ((entry["author"].str.empty) ? entry["editor"].str : entry["author"].str),
            entry["fulltitle"].str,
            ((entry["journal"].str.empty) ? "" : ", " ~ mkup.ff_i ~ mkup.italic ~ mkup.ff_o ~ entry["journal"].str ~ mkup.ff_c ~ mkup.italic),
            ((entry["volume"].str.empty) ? "" : ", " ~ entry["volume"].str),
            ((entry["in"].str.empty) ? "" : ", " ~ entry["in"].str),
            ((!(entry["author"].str.empty) && (!(entry["editor"].str.empty))) ? entry["editor"].str : ""),
            ", " ~ entry["year"].str,
            ((entry["pages"].str.empty) ? "" : ", " ~ entry["pages"].str),
            ((entry["publisher"].str.empty) ? "" : ", " ~ entry["publisher"].str),
            ((entry["place"].str.empty) ? "" : ", " ~ entry["place"].str),
            ((entry["url"].str.empty) ? "" : ", [" ~ entry["url"].str ~ "]"),
          );
          comp_obj_                                   = set_object_generic("backmatter", "bibliography", "para", "bibliography", out_.to!string.strip, 0);
          comp_obj_.metainfo.identifier               = "";
          comp_obj_.metainfo.object_number_off        = true;
          comp_obj_.metainfo.object_number_type       = 0;
          comp_obj_.attrib.indent_hang                = 0;
          comp_obj_.attrib.indent_base                = 1;
          comp_obj_.attrib.bullet                     = bullet;
          comp_obj_.tags.anchor_tags                  = [anchor_tag];
          the_document_bibliography_section           ~= comp_obj_;
        }
      }
    } else {
      comp_obj_                                   = set_object_heading("lev1", "empty", "empty", "(skip) there is no Bibliography");
      comp_obj_.metainfo.identifier               = "";
      comp_obj_.metainfo.dummy_heading            = true;
      comp_obj_.metainfo.object_number_off        = true;
      comp_obj_.metainfo.object_number_type       = 0;
      the_document_bibliography_section           ~= comp_obj_;
    }
    debug(bibliosection) { foreach (o; the_document_bibliography_section) { writeln(o.text); } }
    ST_biblio_section ret;
    {
      ret.bibliography_section = the_document_bibliography_section;
      ret.tag_assoc            = tag_assoc; //
    }
    return ret;
  }
  struct Bibliography {
    @system ST_flow_bibliography flow_bibliography_()(
      string[]    biblio_unsorted_incomplete,
      JSONValue[] bib_arr_json
    ) {
      JSONValue[] biblio_unsorted
        = biblio_make_unsorted_array_of_json_objects(biblio_unsorted_incomplete, bib_arr_json); // TODO lookat returns
      biblio_arr_json = [];
      biblio_unsorted_incomplete = [];
      JSONValue[] biblio_sorted__ = biblio_sort(biblio_unsorted);
      debug(biblio0) {
        biblio_debug(biblio_sorted__);
        writeln("---");
        writeln("unsorted incomplete: ", biblio_unsorted_incomplete.length);
        writeln("json:                ", bib_arr_json.length);
        writeln("unsorted:            ", biblio_unsorted.length);
        writeln("sorted:              ", biblio_sorted__.length);
        int cntr;
        int[7] x;
        while (cntr < x.length) {
          writeln(cntr, ": ", biblio_sorted__[cntr]["fulltitle"]);
          cntr++;
        }
      }
      ST_flow_bibliography ret;
      {
        ret.biblio_sorted  = biblio_sorted__;
        ret.bib_arr_json  = bib_arr_json;
        ret.biblio_unsorted_incomplete  = biblio_unsorted_incomplete;
      }
      return ret;
    }
    @system final private JSONValue[] biblio_make_unsorted_array_of_json_objects()(
      string[]      biblio_unordered,
      JSONValue[]   bib_arr_json
    ) {
      foreach (bibent; biblio_unordered) {
        // update bib to include deemed_author, needed for:
        // sort_bibliography_array_by_deemed_author_year_title
        // either: sort on multiple fields, or; create such sort field
        JSONValue j = parseJSON(bibent);
        if (!empty(j["fulltitle"].str)) {
          if (!empty(j["author_raw"].str)) {
            j["deemed_author"] = j["author_arr"][0];
          } else if (!empty(j["editor_raw"].str)) {
            j["deemed_author"] = j["editor_arr"][0];
          }
          j["sortby_deemed_author_year_title"] = (
            j["deemed_author"].str ~
             "; " ~
             j["year"].str ~
             "; "  ~
             j["fulltitle"].str
          );
        }
        bib_arr_json ~= j;
      }
      return bib_arr_json.dup;
    }
    @system final private JSONValue[] biblio_sort()(JSONValue[] biblio_unordered) {
      JSONValue[] biblio_sorted_;
      biblio_sorted_
        = sort!((a, b){
          return ((a["sortby_deemed_author_year_title"].str) < (b["sortby_deemed_author_year_title"].str));
        })(biblio_unordered).array;
      debug(bibliosorted) {
        foreach (j; biblio_sorted_) {
          if (!empty(j["fulltitle"].str)) { writeln(j["sortby_deemed_author_year_title"]); }
        }
      }
      return biblio_sorted_;
    }
    @system void biblio_debug()(JSONValue[] biblio_sorted) {
      debug(biblio0) {
        foreach (j; biblio_sorted) {
          if (!empty(j["fulltitle"].str)) { writeln(j["sortby_deemed_author_year_title"]); }
        }
      }
    }
  }
  // ↑ - section bibliography
  // ↓ - section blurb
  ST_the_section build_the_blurb_section(Opt) (
    char[]                 line,             // line is immutable, not necessary to return unchanged
    uint[string]           pith,             // double check, should not be necessary to pass pith
    string[string][string] tag_assoc,        // only for headings: html & epub
    Opt                    opt_action,
  ) {
    static auto rgx = RgxI();
    ObjGenericComposite comp_obj_;
    ObjGenericComposite[] add_to_current_document_section;
    // assert (opt_action.backmatter && opt_action.section_blurb);
    indent = [
      "hang_position" : 0,
      "base_position" : 0,
    ];
    bullet = false;
    if (auto m = line.matchFirst(rgx.para_indent)) {
      debug(paraindent) { writeln(line); }
      indent["hang_position"] = (m["indent"]).to!int;
      indent["base_position"] = (m["indent"]).to!int;
    } else if (line.matchFirst(rgx.para_bullet)) {
      debug(parabullet) { writeln(line); }
      bullet = true;
    } else if (auto m = line.matchFirst(rgx.para_indent_hang)) {
      debug(paraindenthang) { writeln(line); }
      indent = [
        "hang_position" : (m["hang"]).to!int,
        "base_position" : (m["indent"]).to!int,
      ];
    } else if (auto m = line.matchFirst(rgx.para_bullet_indent)) {
      debug(parabulletindent) { writeln(line); }
      indent = [
        "hang_position" : (m["indent"]).to!int,
        "base_position" : (m["indent"]).to!int,
      ];
      bullet = true;
    }
    pith["txt_is"]           = eN.txt_is.para;
    line_occur["para"]       = eN.bi.off;
    an_object_key = "blurb_nugget";
    ST_the_section ret;
    if (line.matchFirst(rgx.heading_blurb)
    && (opt_action.backmatter && opt_action.section_blurb)) {
      {
        comp_obj_                                              = set_object_heading("lev1", "backmatter", "blurb", "Blurb");
        comp_obj_.metainfo.identifier                          = "";
        comp_obj_.metainfo.dummy_heading                       = false;
        comp_obj_.metainfo.object_number_off                   = false;
        comp_obj_.metainfo.object_number_type                  = 0;
        comp_obj_.tags.segment_anchor_tag_epub                 = "_part_blurb";
        comp_obj_.tags.anchor_tag_html                         = comp_obj_.tags.segment_anchor_tag_epub;
        comp_obj_.tags.in_segment_html                         = "blurb";
        comp_obj_.tags.anchor_tags                             = ["section_blurb"];
        comp_obj_.metainfo.dom_structure_markedup_tags_status  = [ 1, 1, 0, 0, 0, 0, 0, 0];
        comp_obj_.metainfo.dom_structure_collapsed_tags_status = [ 1, 1, 0, 0, 0, 0, 0, 0];
        add_to_current_document_section                                 ~= comp_obj_;
        tag_assoc[comp_obj_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_.tags.in_segment_html;
        tag_assoc[comp_obj_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_.tags.segment_anchor_tag_epub;
      }
      {
        comp_obj_                                              = set_object_heading("lev4", "backmatter", "blurb", "Blurb");
        comp_obj_.metainfo.identifier                          = "";
        comp_obj_.metainfo.dummy_heading                       = true;
        comp_obj_.metainfo.object_number_off                   = true;
        comp_obj_.metainfo.object_number_type                  = 0;
        comp_obj_.tags.segment_anchor_tag_epub                 = "blurb";
        comp_obj_.tags.anchor_tag_html                         = comp_obj_.tags.segment_anchor_tag_epub;
        comp_obj_.tags.in_segment_html                         = comp_obj_.tags.anchor_tag_html;
        comp_obj_.metainfo.heading_lev_collapsed               = 2;
        comp_obj_.tags.anchor_tags                             = ["blurb"];
        add_to_current_document_section                                 ~= comp_obj_;
        tag_assoc[comp_obj_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_.tags.in_segment_html;
        tag_assoc[comp_obj_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_.tags.segment_anchor_tag_epub;
      }
    } else if (line.matchFirst(rgx.headings)
    && (opt_action.backmatter && opt_action.section_blurb)) {
      comp_obj_                                              = comp_obj_.init;
      comp_obj_.metainfo.is_of_part                          = "backmatter";
      comp_obj_.metainfo.is_of_section                       = "blurb";
      comp_obj_.metainfo.is_of_type                          = "para";
      comp_obj_.metainfo.is_a                                = "heading";
      comp_obj_.text                                         = line.to!string;
      comp_obj_.metainfo.ocn                                 = 0;
      comp_obj_.metainfo.identifier                          = "";
      comp_obj_.metainfo.dummy_heading                       = false;
      comp_obj_.metainfo.object_number_off                   = true;
      comp_obj_.metainfo.object_number_type                  = 0;
      comp_obj_.tags.segment_anchor_tag_epub                 = "blurb";
      comp_obj_.tags.anchor_tag_html                         = comp_obj_.tags.segment_anchor_tag_epub;
      comp_obj_.tags.in_segment_html                         = comp_obj_.tags.anchor_tag_html;
      comp_obj_.metainfo.heading_lev_markup                  = an_object["lev_markup_number"].to!int;    // make int, remove need to conv
      comp_obj_.metainfo.heading_lev_collapsed               = an_object["lev_collapsed_number"].to!int; // make int, remove need to conv
      comp_obj_.metainfo.parent_ocn                          = 1;
      comp_obj_.metainfo.parent_lev_markup                   = 0;
      add_to_current_document_section                                 ~= comp_obj_;
      tag_assoc[comp_obj_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_.tags.in_segment_html;
      tag_assoc[comp_obj_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_.tags.segment_anchor_tag_epub;
    } else if (!(line.empty)) {
      {
        ST_flow_para_match _get = line.flow_para_match_(an_object, an_object_key, indent, bullet, pith, line_occur);
        {
          an_object     = _get.this_object;
          an_object_key = _get.this_object_key;
          pith          = _get.pith;
          indent        = _get.indent;
          bullet        = _get.bullet;
          line_occur    = _get.line_occur;
        }
      }
      comp_obj_                               = set_object_generic("backmatter", "blurb", "para", "blurb", links_and_images(line.to!string.strip).replaceFirst(rgx.para_attribs, ""), 0);
      comp_obj_.metainfo.identifier           = "";
      comp_obj_.metainfo.object_number_off    = true;
      comp_obj_.metainfo.object_number_type   = 0;
      comp_obj_.attrib.indent_hang            = indent["hang_position"];
      comp_obj_.attrib.indent_base            = indent["base_position"];
      comp_obj_.has.inline_links              = true;
      comp_obj_.attrib.bullet                 = bullet;
      add_to_current_document_section         ~= comp_obj_;
    }
    pith["ocn"] = eN.ocn.on;
    {
      ret.comp_section_obj = add_to_current_document_section;
      ret.pith             = pith;
      ret.tag_assoc        = tag_assoc; // NO CHANGE here, only for headings: html & epub
    }
    return ret;
  }
  // ↑ - section blurb
  // ↓ - images
  string[] extract_images()(string content_block) {
    static auto rgx = RgxI();
    string[] images_;
    if (auto m = content_block.matchAll(rgx.image)) {
      images_ ~= m.captures[1];
    }
    return images_;
  }
  @system auto _image_dimensions(O,M)(O obj, M manifested) {
    static auto rgx = RgxI();
    if (obj.has.image_without_dimensions) {
      import std.math;
      import imageformats;
      int w, h, chans;
      real _w, _h;
      int max_width = 640;
      foreach (img; obj.text.matchAll(rgx.inline_image_without_dimensions)) {
        try {
          read_image_info(manifested.src.image_dir_path ~ "/" ~ img["img"], w, h, chans);
        } catch (Exception ex) {
          writeln("WARNING, image not found: ", img["img"], "\n  ", manifested.src.image_dir_path ~ "/" ~ img["img"]);
        }
        // calculate, decide max width and proportionally reduce to keep w & h within it
        debug(images) { writeln("width: ", w, ", height: ", h); }
        if (w > max_width) {
          _w = max_width;
          _h = round((max_width / w.to!real) * h.to!real);
        } else {
          _w = w;
          _h = h;
        }
        obj.text = obj.text.replaceFirst(
          rgx.inline_image_without_dimensions,
          format(q"┃%s☼%s,w%sh%s %s┃",
            "$1",
            "$3",
            _w.to!string,
            _h.to!string,
            "$6",
          )
        );
      }
      debug(images) { writeln("image without dimensions: ", obj.text); }
    }
    return obj;
  }
  // ↑ - images
  // ↓ - links
  auto _links(O)(O obj) {
    static auto rgx = RgxI();
    if (auto m = obj.text.match(rgx.inline_link_stow_uri)) {
      debug(links) {
        writeln("number of link matches to stow: ", (obj.text.match(rgx.inline_link_stow_uri)).count);
        writeln("links to stow: ", (obj.text.match(rgx.inline_link_stow_uri)));
      }
      int _n_matches = (obj.text.match(rgx.inline_link_stow_uri)).count.to!int;
      for(int i = 0; i < _n_matches; ++i) {
        if (obj.text.match(rgx.inline_link_stow_uri)) {
          obj.stow.link ~= obj.text.matchFirst(rgx.inline_link_stow_uri)[2];
          obj.text = obj.text.replaceFirst(
            rgx.inline_link_stow_uri,
            format(q"┃┥%s┝┤%s├┃", "$1", i)
          );
        }
      }
    }
    return obj;
  }
  // ↑ - links
  // ↓ - segnames
  @system ST_segnames after_doc_determine_segnames(
    ObjGenericComposite[] the_document_body_section,
    ObjGenericComposite[] the_document_endnotes_section,
    ObjGenericComposite[] the_document_glossary_section,
    ObjGenericComposite[] the_document_bibliography_section,
    ObjGenericComposite[] the_document_bookindex_section,
    ObjGenericComposite[] the_document_blurb_section,
    string[][string]      segnames,
    int                   html_segnames_ptr_cntr,
    int                   html_segnames_ptr,
  ) {
    if (the_document_endnotes_section.length > 1) {
      segnames["html"] ~= "endnotes";
      segnames["epub"] ~= "endnotes";
      html_segnames_ptr = html_segnames_ptr_cntr;
      foreach (ref obj; the_document_endnotes_section) {
        if (obj.metainfo.is_a == "heading") {
          obj.metainfo.parent_ocn = obj.metainfo.markedup_ancestors[obj.metainfo.parent_lev_markup];
        }
        if (obj.metainfo.heading_lev_markup == 4) {
          obj.ptr.html_segnames = html_segnames_ptr;
          break;
        }
      }
      html_segnames_ptr_cntr++;
    }
    if (the_document_glossary_section.length > 1) {
      segnames["html"] ~= "glossary";
      segnames["epub"] ~= "glossary";
      html_segnames_ptr = html_segnames_ptr_cntr;
      foreach (ref obj; the_document_glossary_section) {
        if (obj.metainfo.is_a == "heading") {
          obj.metainfo.parent_ocn = obj.metainfo.markedup_ancestors[obj.metainfo.parent_lev_markup];
        }
        if (obj.metainfo.heading_lev_markup == 4) {
          obj.ptr.html_segnames = html_segnames_ptr;
          break;
        }
      }
      html_segnames_ptr_cntr++;
    }
    if (the_document_bibliography_section.length > 1) {
      segnames["html"] ~= "bibliography";
      segnames["epub"] ~= "bibliography";
      html_segnames_ptr = html_segnames_ptr_cntr;
      foreach (ref obj; the_document_bibliography_section) {
        if (obj.metainfo.is_a == "heading") {
          obj.metainfo.parent_ocn = obj.metainfo.markedup_ancestors[obj.metainfo.parent_lev_markup];
        }
        if (obj.metainfo.heading_lev_markup == 4) {
          obj.ptr.html_segnames = html_segnames_ptr;
          break;
        }
      }
      html_segnames_ptr_cntr++;
    }
    if (the_document_bookindex_section.length > 1) {
      segnames["html"] ~= "bookindex";
      segnames["epub"] ~= "bookindex";
      html_segnames_ptr = html_segnames_ptr_cntr;
      foreach (ref obj; the_document_bookindex_section) {
        if (obj.metainfo.is_a == "heading") {
          obj.metainfo.parent_ocn = obj.metainfo.markedup_ancestors[obj.metainfo.parent_lev_markup];
        }
        if (obj.metainfo.heading_lev_markup == 4) {
          obj.ptr.html_segnames = html_segnames_ptr;
          break;
        }
      }
      html_segnames_ptr_cntr++;
    }
    if (the_document_blurb_section.length > 1) {
      segnames["html"] ~= "blurb";
      segnames["epub"] ~= "blurb";
      html_segnames_ptr = html_segnames_ptr_cntr;
      foreach (ref obj; the_document_blurb_section) {
        if (obj.metainfo.is_a == "heading") {
          obj.metainfo.parent_ocn = obj.metainfo.markedup_ancestors[obj.metainfo.parent_lev_markup];
        }
        if (obj.metainfo.heading_lev_markup == 4) {
          obj.ptr.html_segnames = html_segnames_ptr;
          break;
        }
      }
      html_segnames_ptr_cntr++;
    }
    ST_segnames ret;
    ret.segnames                          = segnames;
    ret.html_segnames_ptr_cntr            = html_segnames_ptr_cntr;
    ret.html_segnames_ptr                 = html_segnames_ptr;
    return ret;
  }
  // ↑ - segnames
  // ↓ - ancestors descendants
  struct NodeStructureMetadata {
    int lv, lv0, lv1, lv2, lv3, lv4, lv5, lv6, lv7;
    int obj_cite_digit;
    int[string] p_; // p_ parent_
    static auto rgx = RgxI();
    ObjGenericComposite node_location_emitter(La,Ta)(
      string         lev_markup_number,
      string[string] tag_in_seg,
      La             lev_anchor_tag,
      Ta             tag_assoc,
      OCNset         obj_cite_digits,
      int            cntr_,
      int            ptr_,
      string         is_
    ) {
      debug(asserts) { static assert(is(typeof(obj_cite_digits.object_number) == int)); }
      assert(is_ != "heading");
      assert(obj_cite_digits.object_number.to!int >= 0);
      assert(is_ != "heading");                          // should not be necessary
      assert(obj_cite_digits.object_number.to!int >= 0); // should not be necessary
      if (lv7 > eN.bi.off) {
        p_["lev_markup_number"]                       = DocStructMarkupHeading.h_text_4;
        p_["object_number"]                           = lv7;
      } else if (lv6 > eN.bi.off) {
        p_["lev_markup_number"]                       = DocStructMarkupHeading.h_text_3;
        p_["object_number"]                           = lv6;
      } else if (lv5 > eN.bi.off) {
        p_["lev_markup_number"]                       = DocStructMarkupHeading.h_text_2;
        p_["object_number"]                           = lv5;
      } else {
        p_["lev_markup_number"]                       = DocStructMarkupHeading.h_text_1;
        p_["object_number"]                           = lv4;
      }
      ObjGenericComposite comp_obj_location;
      comp_obj_location                               = comp_obj_location.init;
      comp_obj_location.metainfo.is_a                 = is_;
      comp_obj_location.metainfo.ocn                  = obj_cite_digits.object_number;
      comp_obj_location.metainfo.identifier           = obj_cite_digits.identifier;
      comp_obj_location.tags.anchor_tag_html          = tag_in_seg["seg_lv4"];
      comp_obj_location.tags.segment_anchor_tag_epub  = tag_in_seg["seg_lv1to4"];
      comp_obj_location.tags.heading_lev_anchor_tag   = lev_anchor_tag;
      comp_obj_location.metainfo.parent_ocn           = p_["object_number"];
      comp_obj_location.metainfo.parent_lev_markup    = p_["lev_markup_number"];
      debug(_node) {
        if (lev_markup_number.match(rgx.levels_numbered_headings)) { writeln("x ", _node.to!string);
        } else { writeln("- ", _node.to!string); }
      }
      assert(comp_obj_location.metainfo.parent_lev_markup >= 4);
      assert(comp_obj_location.metainfo.parent_lev_markup <= 7);
      assert(comp_obj_location.metainfo.parent_ocn >= 0);
      return comp_obj_location;
    }
    invariant() {
    }
    ObjGenericComposite node_emitter_heading(O,TaL,TA,SOAT)(
      O              an_object,
      string[string] tag_in_seg,
      TaL            lev_anchor_tag,
      TA             tag_assoc,
      OCNset         obj_cite_digits,
      int            cntr_,
      int            ptr_,
      string[]       lv_ancestors_txt,
      int            html_segnames_ptr,
      SOAT           substantive_object_and_anchor_tags_struct,
    ) {
      string _text                = an_object["substantive"];
      string lev_markup_number    = an_object["lev_markup_number"];
      string lev_collapsed_number = an_object["lev_collapsed_number"];
      string dummy_heading_status = an_object["dummy_heading_status"];
      string is_                  = an_object["is"];
      debug(asserts) {
        static assert(is(typeof(lev)                                       == string));
        static assert(is(typeof(obj_cite_digits.object_number)             == int));
      }
      assert(is_ == "heading");
      assert((obj_cite_digits.object_number).to!int >= 0);
      assert(
        lev_markup_number.match(rgx.levels_numbered),
        ("not a valid heading level: " ~ lev_markup_number ~ " at " ~ obj_cite_digits.object_number.to!string)
      );
      if (lev_markup_number.match(rgx.levels_numbered)) {
        if (lev_markup_number.to!int == 0) {
          // TODO first hit (of two) with this assertion failure, check, fix & reinstate
          // assert(obj_cite_digits.object_number.to!int == 1,
          //   "ERROR header lev markup number is: " ~
          //   lev_markup_number.to!string ~
          //   " obj_cite_digits.object_number.to!int should == 1 but is: " ~
          //    obj_cite_digits.object_number.to!string ~
          //   "\n" ~ _text);
        }
      }
      switch (lev_markup_number.to!int) {
      case 0:
        lv = DocStructMarkupHeading.h_sect_A;
        lv0 = obj_cite_digit;
        lv1 = 0; lv2 = 0; lv3 = 0; lv4 = 0; lv5 = 0; lv6 = 0; lv7 = 0;
        p_["lev_markup_number"] = 0;
        p_["object_number"] = 0;
        break;
      case 1:
        lv = DocStructMarkupHeading.h_sect_B;
        lv1 = obj_cite_digit;
        lv2 = 0; lv3 = 0; lv4 = 0; lv5 = 0; lv6 = 0; lv7 = 0;
        p_["lev_markup_number"]
          = DocStructMarkupHeading.h_sect_A;
        p_["object_number"] = lv0;
        break;
      case 2:
        lv = DocStructMarkupHeading.h_sect_C;
        lv2 = obj_cite_digit;
        lv3 = 0; lv4 = 0; lv5 = 0; lv6 = 0; lv7 = 0;
        p_["lev_markup_number"]
          = DocStructMarkupHeading.h_sect_B;
        p_["object_number"] = lv1;
        break;
      case 3:
        lv = DocStructMarkupHeading.h_sect_D;
        lv3 = obj_cite_digit;
        lv4 = 0; lv5 = 0; lv6 = 0; lv7 = 0;
        p_["lev_markup_number"]
          = DocStructMarkupHeading.h_sect_C;
        p_["object_number"] = lv2;
        break;
      case 4:
        lv = DocStructMarkupHeading.h_text_1;
        lv4 = obj_cite_digit;
        lv5 = 0; lv6 = 0; lv7 = 0;
        if (lv3 > eN.bi.off) {
          p_["lev_markup_number"]
            = DocStructMarkupHeading.h_sect_D;
          p_["object_number"] = lv3;
        } else if (lv2 > eN.bi.off) {
          p_["lev_markup_number"]
            = DocStructMarkupHeading.h_sect_C;
          p_["object_number"] = lv2;
        } else if (lv1 > eN.bi.off) {
          p_["lev_markup_number"]
            = DocStructMarkupHeading.h_sect_B;
          p_["object_number"] = lv1;
        } else {
          p_["lev_markup_number"]
            = DocStructMarkupHeading.h_sect_A;
          p_["object_number"] = lv0;
        }
        break;
      case 5:
        lv = DocStructMarkupHeading.h_text_2;
        lv5 = obj_cite_digit;
        lv6 = 0; lv7 = 0;
        p_["lev_markup_number"]
          = DocStructMarkupHeading.h_text_1;
        p_["object_number"] = lv4;
        break;
      case 6:
        lv = DocStructMarkupHeading.h_text_3;
        lv6 = obj_cite_digit;
        lv7 = 0;
        p_["lev_markup_number"]
          = DocStructMarkupHeading.h_text_2;
        p_["object_number"] = lv5;
        break;
      case 7:
        lv = DocStructMarkupHeading.h_text_4;
        lv7 = obj_cite_digit;
        p_["lev_markup_number"]
          = DocStructMarkupHeading.h_text_3;
        p_["object_number"] = lv6;
        break;
      default:
        break;
      }
      ObjGenericComposite comp_obj_;
      comp_obj_                                        = comp_obj_.init;
      comp_obj_.metainfo.is_of_part                    = "body";
      comp_obj_.metainfo.is_of_section                 = "body";
      comp_obj_.metainfo.is_of_type                    = "para";
      comp_obj_.metainfo.is_a                          = "heading";
      comp_obj_.text                                   = _text.to!string.strip;
      comp_obj_.metainfo.ocn                           = obj_cite_digits.object_number;
      comp_obj_.metainfo.identifier                    = obj_cite_digits.identifier;
      comp_obj_.metainfo.dummy_heading                 = (dummy_heading_status == "t") ? true: false;
      comp_obj_.metainfo.object_number_off             = obj_cite_digits.off;
      // comp_obj_.metainfo.o_n_book_index             = obj_cite_digits.bkidx;
      comp_obj_.metainfo.object_number_type            = obj_cite_digits.type;
      comp_obj_.tags.segment_anchor_tag_epub           = tag_in_seg["seg_lv1to4"];
      comp_obj_.tags.anchor_tag_html                   = tag_in_seg["seg_lv4"];
      comp_obj_.tags.in_segment_html                   = comp_obj_.tags.anchor_tag_html;
      comp_obj_.tags.heading_lev_anchor_tag            = lev_anchor_tag;
      comp_obj_.tags.html_segment_anchor_tag_is        = tag_in_seg["seg_lv4"];
      comp_obj_.tags.epub_segment_anchor_tag_is        = tag_in_seg["seg_lv1to4"];
      comp_obj_.metainfo.heading_lev_markup            = (!(lev_markup_number.empty) ? lev_markup_number.to!int : 0);
      comp_obj_.metainfo.heading_lev_collapsed         = (!(lev_collapsed_number.empty) ? lev_collapsed_number.to!int : 0);
      comp_obj_.metainfo.parent_ocn                    = p_["object_number"];
      comp_obj_.metainfo.parent_lev_markup             = p_["lev_markup_number"];
      comp_obj_.tags.heading_ancestors_text            = lv_ancestors_txt;
      comp_obj_.ptr.doc_object                         = cntr_;
      comp_obj_.ptr.html_segnames                      = ((lev_markup_number == "4") ? html_segnames_ptr : 0);
      comp_obj_.ptr.heading                            = ptr_;
      comp_obj_.has.inline_notes_reg                   = substantive_object_and_anchor_tags_struct.has_notes_reg;
      comp_obj_.has.inline_notes_star                  = substantive_object_and_anchor_tags_struct.has_notes_star;
      comp_obj_.has.inline_links                       = substantive_object_and_anchor_tags_struct.has_links;
      tag_assoc[comp_obj_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_.tags.in_segment_html;
      tag_assoc[comp_obj_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_.tags.segment_anchor_tag_epub;
      debug(_node) {
        if (lev_markup_number.match(rgx.levels_numbered_headings)) { writeln("* ", _node.to!string); }
      }
      debug(nodeheading) {
        if (lev_markup_number.match(rgx.levels_numbered_headings)) { writeln("* ", _node.to!string); }
      }
      assert(comp_obj_.metainfo.parent_lev_markup <= 7);
      assert(comp_obj_.metainfo.parent_ocn >= 0);
      if (lev_markup_number.match(rgx.levels_numbered_headings)) {
        assert(comp_obj_.metainfo.heading_lev_markup <= 7);
        assert(comp_obj_.metainfo.ocn >= 0);
        if (comp_obj_.metainfo.parent_lev_markup > 0) {
          assert(comp_obj_.metainfo.parent_lev_markup < comp_obj_.metainfo.heading_lev_markup);
          if (comp_obj_.metainfo.ocn != 0) {
            assert(comp_obj_.metainfo.parent_ocn < comp_obj_.metainfo.ocn);
          }
        }
        if (comp_obj_.metainfo.heading_lev_markup == 0) {
          assert(comp_obj_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_sect_A);
        } else if  (comp_obj_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_sect_B) {
          assert(comp_obj_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_sect_A);
        } else if  (comp_obj_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_sect_C) {
          assert(comp_obj_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_sect_B);
        } else if  (comp_obj_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_sect_D) {
          assert(comp_obj_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_sect_C);
        } else if  (comp_obj_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_text_1) {
          assert(comp_obj_.metainfo.parent_lev_markup <= DocStructMarkupHeading.h_sect_D);
        } else if  (comp_obj_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_text_2) {
          assert(comp_obj_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_text_1);
        } else if  (comp_obj_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_text_3) {
          assert(comp_obj_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_text_2);
        } else if  (comp_obj_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_text_4) {
          assert(comp_obj_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_text_3);
        } else if  (comp_obj_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_text_5) {
        }
      }
      return comp_obj_;
    }
    invariant() {
    }
  }
  @system ST_ancestors after_doc_determine_ancestors(
    ObjGenericComposite[] the_document_body_section,
    ObjGenericComposite[] the_document_endnotes_section,
    ObjGenericComposite[] the_document_glossary_section,
    ObjGenericComposite[] the_document_bibliography_section,
    ObjGenericComposite[] the_document_bookindex_section,
    ObjGenericComposite[] the_document_blurb_section,
  ) {
    int[] _get_ancestors_markup(ObjGenericComposite obj, int[] _ancestors_markup) {
      if (obj.metainfo.is_a == "heading") {
        debug(dom) { writeln(obj.text); }
        if (obj.metainfo.heading_lev_markup == 1) {
          _ancestors_markup = [
            _ancestors_markup[0],
            0,0,0,0,0,0,0
          ];
        }
        if (obj.metainfo.heading_lev_markup == 2) {
          _ancestors_markup = [
            _ancestors_markup[0],
            _ancestors_markup[1],
            0,0,0,0,0,0
          ];
        }
        if (obj.metainfo.heading_lev_markup == 3) {
          _ancestors_markup = [
            _ancestors_markup[0],
            _ancestors_markup[1],
            _ancestors_markup[2],
            0,0,0,0,0
          ];
        }
        if (obj.metainfo.heading_lev_markup == 4) {
          _ancestors_markup = [
            _ancestors_markup[0],
            _ancestors_markup[1],
            _ancestors_markup[2],
            _ancestors_markup[3],
            0,0,0,0
          ];
        }
        if (obj.metainfo.heading_lev_markup == 5) {
          _ancestors_markup = [
            _ancestors_markup[0],
            _ancestors_markup[1],
            _ancestors_markup[2],
            _ancestors_markup[3],
            _ancestors_markup[4],
            0,0,0
          ];
        }
        if (obj.metainfo.heading_lev_markup == 6) {
          _ancestors_markup = [
            _ancestors_markup[0],
            _ancestors_markup[1],
            _ancestors_markup[2],
            _ancestors_markup[3],
            _ancestors_markup[4],
            _ancestors_markup[5],
            0,0
          ];
        }
        if (obj.metainfo.heading_lev_markup == 7) {
          _ancestors_markup = [
            _ancestors_markup[0],
            _ancestors_markup[1],
            _ancestors_markup[2],
            _ancestors_markup[3],
            _ancestors_markup[4],
            _ancestors_markup[5],
            _ancestors_markup[6],
            0
          ];
        }
        if (obj.metainfo.heading_lev_markup == 8) {
          _ancestors_markup = [
            _ancestors_markup[0],
            _ancestors_markup[1],
            _ancestors_markup[2],
            _ancestors_markup[3],
            _ancestors_markup[4],
            _ancestors_markup[5],
            _ancestors_markup[6],
            _ancestors_markup[7]
          ];
        }
        _ancestors_markup[obj.metainfo.heading_lev_markup] = obj.metainfo.ocn;
      }
      debug(ancestor_markup) { writeln("marked up: ", _ancestors_markup); }
      return _ancestors_markup;
    }
    int[] _get_ancestors_collapsed(ObjGenericComposite obj, int[] _ancestors_collapsed) {
      if (obj.metainfo.is_a == "heading") {
        if (obj.metainfo.heading_lev_collapsed == 1) {
          _ancestors_collapsed = [
            _ancestors_collapsed[0],
            0,0,0,0,0,0,0
          ];
        }
        if (obj.metainfo.heading_lev_collapsed == 2) {
          _ancestors_collapsed = [
            _ancestors_collapsed[0],
            _ancestors_collapsed[1],
            0,0,0,0,0,0
          ];
        }
        if (obj.metainfo.heading_lev_collapsed == 3) {
          _ancestors_collapsed = [
            _ancestors_collapsed[0],
            _ancestors_collapsed[1],
            _ancestors_collapsed[2],
            0,0,0,0,0
          ];
        }
        if (obj.metainfo.heading_lev_collapsed == 4) {
          _ancestors_collapsed = [
            _ancestors_collapsed[0],
            _ancestors_collapsed[1],
            _ancestors_collapsed[2],
            _ancestors_collapsed[3],
            0,0,0,0
          ];
        }
        if (obj.metainfo.heading_lev_collapsed == 5) {
          _ancestors_collapsed = [
            _ancestors_collapsed[0],
            _ancestors_collapsed[1],
            _ancestors_collapsed[2],
            _ancestors_collapsed[3],
            _ancestors_collapsed[4],
            0,0,0
          ];
        }
        if (obj.metainfo.heading_lev_collapsed == 6) {
          _ancestors_collapsed = [
            _ancestors_collapsed[0],
            _ancestors_collapsed[1],
            _ancestors_collapsed[2],
            _ancestors_collapsed[3],
            _ancestors_collapsed[4],
            _ancestors_collapsed[5],
            0,0
          ];
        }
        if (obj.metainfo.heading_lev_collapsed == 7) {
          _ancestors_collapsed = [
            _ancestors_collapsed[0],
            _ancestors_collapsed[1],
            _ancestors_collapsed[2],
            _ancestors_collapsed[3],
            _ancestors_collapsed[4],
            _ancestors_collapsed[5],
            _ancestors_collapsed[6],
            0
          ];
        }
        if (obj.metainfo.heading_lev_collapsed == 8) {
          _ancestors_collapsed = [
            _ancestors_collapsed[0],
            _ancestors_collapsed[1],
            _ancestors_collapsed[2],
            _ancestors_collapsed[3],
            _ancestors_collapsed[4],
            _ancestors_collapsed[5],
            _ancestors_collapsed[6],
            _ancestors_collapsed[7]
          ];
        }
        _ancestors_collapsed[obj.metainfo.heading_lev_collapsed] = obj.metainfo.ocn;
      }
      debug(ancestor_collapsed) { writeln("collapsed: ", _ancestors_collapsed); }
      return _ancestors_collapsed;
    }
    // multiple 1~ levels, loop through document body
    if (the_document_body_section.length > 1) {
      int[] _ancestors_markup = [0,0,0,0,0,0,0,0];
      int[][] _ancestors_markup_;
      _ancestors_markup = [1,0,0,0,0,0,0,0];
      _ancestors_markup_ ~= _ancestors_markup;
      int[] _ancestors_collapsed = [0,0,0,0,0,0,0,0];
      int[][] _ancestors_collapsed_;
      _ancestors_collapsed = [1,0,0,0,0,0,0,0];
      _ancestors_collapsed_ ~= _ancestors_collapsed;
      foreach (ref obj; the_document_body_section) {
        if (obj.metainfo.is_a == "heading") {
          obj.metainfo.markedup_ancestors = _get_ancestors_markup(obj, _ancestors_markup);
          obj.metainfo.collapsed_ancestors = _get_ancestors_collapsed(obj, _ancestors_collapsed);
          obj.metainfo.parent_ocn = obj.metainfo.markedup_ancestors[obj.metainfo.parent_lev_markup];
        }
      }
      debug(ancestors) {
        writeln("ancestors markup o_n:    ", obj.metainfo.markedup_ancestors);
        writeln("ancestors collapsed o_n: ", obj.metainfo.markedup_ancestors);
      }
    }
    ST_ancestors ret;
    ret.the_document_body_section         = the_document_body_section;
    ret.the_document_endnotes_section     = the_document_endnotes_section;
    ret.the_document_glossary_section     = the_document_glossary_section;
    ret.the_document_bibliography_section = the_document_bibliography_section;
    ret.the_document_bookindex_section    = the_document_bookindex_section;
    ret.the_document_blurb_section        = the_document_blurb_section;
    return ret;
  }
  // ↑ - ancestors
  // ↓ - descendants
  // descendants
  auto after_doc_get_descendants()(ObjGenericComposite[] document_sections) {
    int[string] _heading_ocn_descendants;
    string[] _ocn_open_key = ["","","","","","","",""];
    auto _doc_sect_length = document_sections.length - 1;
    int _last_ocn;
    foreach (_lg, ref obj; document_sections) {
      if (obj.metainfo.is_a == "heading") {
        foreach (_dts_lv, dom_tag_status; obj.metainfo.dom_structure_markedup_tags_status) {
          switch (dom_tag_status) with (DomTags) {
          case none: break;
          case open:
              _ocn_open_key[_dts_lv] = (obj.metainfo.ocn).to!string;
              _heading_ocn_descendants[_ocn_open_key[_dts_lv]] = obj.metainfo.ocn;
            break;
          case close:
            if (_ocn_open_key[_dts_lv].empty) {
              _ocn_open_key[_dts_lv] = "0";
            }
            _heading_ocn_descendants[_ocn_open_key[_dts_lv]] = obj.metainfo.ocn - 1;
            _ocn_open_key[_dts_lv] = (0).to!string;
            break;
          case close_and_open:
            if (_ocn_open_key[_dts_lv].empty) {
              _ocn_open_key[_dts_lv] = "0";
            }
            _heading_ocn_descendants[_ocn_open_key[_dts_lv]] = obj.metainfo.ocn - 1;
            _ocn_open_key[_dts_lv] = (obj.metainfo.ocn).to!string;
            _heading_ocn_descendants[_ocn_open_key[_dts_lv]] = obj.metainfo.ocn;
            break;
          case open_still: break;
          default: break;
          }
        }
      }
      if (obj.metainfo.ocn > 0) {
        _last_ocn = obj.metainfo.ocn;
      }
      if (_lg == _doc_sect_length) {
        _heading_ocn_descendants["1"] = _last_ocn; // close existing o_n key
      }
    }
    Tuple!(int, int)[] pairs;
    foreach (pair; _heading_ocn_descendants.byPair) {
      pairs ~= tuple(pair[0].to!int, pair[1]);
    }
    return pairs.sort;
  }
  // ↑ - descendants
  // ↓ - assertions
  pure void assertions_doc_structure()(
    string[string]  an_object,
    string          an_object_key,
    int[string]     lv
  ) {
    string msg_error_doc_struct = "\nERROR in document structure, check markup (heading level relationships):\n";
    if (lv["h3"] > eN.bi.off) {
      assert(lv["h0"] > eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h1"] > eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h2"] > eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    } else if (lv["h2"] > eN.bi.off) {
      assert(lv["h0"] > eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h1"] > eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h3"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    } else if (lv["h1"] > eN.bi.off) {
      assert(lv["h0"] > eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h2"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h3"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    } else if (lv["h0"] > eN.bi.off) {
      assert(lv["h1"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h2"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h3"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    } else {
      assert(lv["h0"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h1"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h2"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h3"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    }
    if (lv["h7"] > eN.bi.off) {
      assert(lv["h4"] > eN.bi.off,
        msg_error_doc_struct
        ~ "missing segment level 1~\n"
        ~ an_object[an_object_key]
      );
      assert(lv["h5"] > eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h6"] > eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    } else if (lv["h6"] > eN.bi.off) {
      assert(lv["h4"] > eN.bi.off,
        msg_error_doc_struct
        ~ "missing segment level 1~\n"
        ~ an_object[an_object_key]
      );
      assert(lv["h5"] > eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h7"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    } else if (lv["h5"] > eN.bi.off) {
      assert(lv["h4"] > eN.bi.off,
        msg_error_doc_struct
        ~ "missing segment level 1~\n"
        ~ an_object[an_object_key]
      );
      assert(lv["h6"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h7"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    } else if (lv["h4"] > eN.bi.off) {
      assert(lv["h5"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h6"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h7"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    } else {
      assert(lv["h4"] == eN.bi.off,
        msg_error_doc_struct
        ~ "missing segment level 1~\n"
        ~ an_object[an_object_key]
      );
      assert(lv["h5"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h6"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h7"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    }
    if (lv["h0"] == eN.bi.off) {
      assert(lv["h1"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h2"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h3"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h4"] == eN.bi.off,
        msg_error_doc_struct
        ~ "missing segment level 1~\n"
        ~ an_object[an_object_key]
      );
      assert(lv["h5"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h6"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h7"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    }
    if (lv["h1"] == eN.bi.off) {
      assert(lv["h2"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h3"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    }
    if (lv["h2"] == eN.bi.off) {
      assert(lv["h3"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    }
    if (lv["h3"] == eN.bi.off) {
    }
    if (lv["h4"] == eN.bi.off) {
      assert(lv["h5"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h6"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h7"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    }
    if (lv["h5"] == eN.bi.off) {
      assert(lv["h6"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
      assert(lv["h7"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    }
    if (lv["h6"] == eN.bi.off) {
      assert(lv["h7"] == eN.bi.off,
        msg_error_doc_struct
        ~ an_object[an_object_key]
      );
    }
    if (lv["h7"] == eN.bi.off) {
    }
    switch ((an_object["lev"]).to!string) {
    case "A":
      if (lv["h0"] == eN.bi.off) {
        assert(lv["h1"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level A~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h2"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level A~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h3"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level A~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h4"] == eN.bi.off,
          msg_error_doc_struct
          ~ "missing segment level 1~\n"
          ~ "at level A~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h5"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level A~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h6"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level A~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h7"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level A~\n"
          ~ an_object[an_object_key]
        );
      } else {                       // (lv["h0"] > eN.bi.off)
        assert(lv["h0"] == eN.bi.off,
          msg_error_doc_struct
          ~ "should not enter level A a second time\n"
          ~ "at level A~\n"
          ~ an_object[an_object_key]
        );
      }
      break;
    case "B":
      if (lv["h1"] == eN.bi.off) {
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level B~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h2"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level B~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h3"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level B~\n"
          ~ an_object[an_object_key]
        );
      } else {                       // (lv["h1"] > eN.bi.off)
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level B~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h1"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level B~\n"
          ~ an_object[an_object_key]
        );
      }
      break;
    case "C":
      if (lv["h2"] == eN.bi.off) {
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level C~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h1"] > eN.bi.off,
          msg_error_doc_struct
          ~ "level C should not follow level A\n"
          ~ "at level C~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h3"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level C~\n"
          ~ an_object[an_object_key]
        );
      } else {                       // (lv["h2"] > eN.bi.off)
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level C~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h1"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level C~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h2"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level C~\n"
          ~ an_object[an_object_key]
        );
      }
      break;
    case "D":
      if (lv["h3"] == eN.bi.off) {
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level D~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h1"] > eN.bi.off,
          msg_error_doc_struct
          ~ "level D should not follow level A\n"
          ~ "at level D~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h2"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level D~\n"
          ~ an_object[an_object_key]
        );
      } else {                      // (lv["h3"] > eN.bi.off)
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level D~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h1"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level D~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h2"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level D~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h3"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level D~\n"
          ~ an_object[an_object_key]
        );
      }
      break;
    case "1":
      if (lv["h4"] == eN.bi.off) {
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 1~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h5"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level 1~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h6"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level 1~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h7"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level 1~\n"
          ~ an_object[an_object_key]
        );
      } else {                      // (lv["h4"] > eN.bi.off)
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 1~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h4"] > eN.bi.off,
          msg_error_doc_struct
          ~ "missing segment level 1~ ?\n"
          ~ "at level 1~\n"
          ~ an_object[an_object_key]
        );
      }
      break;
    case "2":
      if (lv["h5"] == eN.bi.off) {
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 2~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h4"] > eN.bi.off,
          msg_error_doc_struct
          ~ "missing segment level 1~ ?\n"
          ~ "at level 2~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h6"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level 2~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h7"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level 2~\n"
          ~ an_object[an_object_key]
        );
      } else {                      // (lv["h5"] > eN.bi.off)
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 2~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h4"] > eN.bi.off,
          msg_error_doc_struct
          ~ "missing segment level 1~ ?\n"
          ~ "at level 2~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h5"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 2~\n"
          ~ an_object[an_object_key]
        );
      }
      break;
    case "3":
      if (lv["h6"] == eN.bi.off) {
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 3~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h4"] > eN.bi.off,
          msg_error_doc_struct
          ~ "missing segment level 1~ ?\n"
          ~ "at level 3~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h5"] > eN.bi.off,
          msg_error_doc_struct
          ~ "missing segment level 2~ ?\n"
          ~ "at level 3~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h7"] == eN.bi.off,
          msg_error_doc_struct
          ~ "at level 3~\n"
          ~ an_object[an_object_key]
        );
      } else {                      // (lv["h6"] > eN.bi.off)
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 3~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h4"] > eN.bi.off,
          msg_error_doc_struct
          ~ "missing segment level 1~ ?\n"
          ~ "at level 3~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h5"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 3~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h6"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 3~\n"
          ~ an_object[an_object_key]
        );
      }
      break;
    case "4":
      if (lv["h7"] == eN.bi.off) {
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 4~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h4"] > eN.bi.off,
          msg_error_doc_struct
          ~ "missing segment level 1~ ?\n"
          ~ "at level 4~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h5"] > eN.bi.off,
          msg_error_doc_struct
          ~ "missing segment level 2~ ?\n"
          ~ "at level 4~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h6"] > eN.bi.off,
          msg_error_doc_struct
          ~ "missing segment level 3~ ?\n"
          ~ "at level 4~\n"
          ~ an_object[an_object_key]
        );
      } else {                      // (lv["h7"] > eN.bi.off)
        assert(lv["h0"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 4~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h4"] > eN.bi.off,
          msg_error_doc_struct
          ~ "missing segment level 1~ ?\n"
          ~ "at level 4~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h5"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 4~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h6"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 4~\n"
          ~ an_object[an_object_key]
        );
        assert(lv["h7"] > eN.bi.off,
          msg_error_doc_struct
          ~ "at level 4~\n"
          ~ an_object[an_object_key]
        );
      }
      break;
    default:
      break;
    }
  }
  // ↑ - assertions
}
template docSectKeysSeq() {
  auto docSectKeysSeq(string[][string] document_section_keys_sequenced) {
    struct doc_sect_keys_seq {
      string[] scroll() {
        return document_section_keys_sequenced["scroll"];
      }
      string[] seg() {
        return document_section_keys_sequenced["seg"];
      }
      string[] sql() {
        return document_section_keys_sequenced["sql"];
      }
      string[] latex() {
        return document_section_keys_sequenced["latex"];
      }
    }
    return doc_sect_keys_seq();
  }
}