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

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

* 1. (Object-Centric) Document Abstraction
Process markup document, create document abstraction.

** _module template_ :module:metadoc_from_src:

#+HEADER: :tangle "../src/doc_reform/meta/metadoc_from_src.d"
#+HEADER: :noweb yes
#+BEGIN_SRC d
<<doc_header_including_copyright_and_license>>
/++
  document abstraction:
  abstraction of sisu markup for downstream processing
  metadoc_from_src.d
+/
module doc_reform.meta.metadoc_from_src;
template docAbstraction() {
                                                                                /+ ↓ abstraction imports +/
  <<abs_top_imports>>
                                                                                /+ ↓ abstraction mixins +/
  <<abs_top_mixins>>
                                                                                /+ ↓ abstraction struct init +/
  <<abs_top_init_struct_enum>>
  <<abs_top_init_struct_general>>
  <<abs_top_init_generic_object_struct_heading_ancestors>>
  <<abs_top_init_generic_object_struct_dom_markup_tags>>
  <<abs_top_init_generic_object_struct_dom_collapsed_tags>>
  <<abs_top_init_ocn_emit>>
  <<abs_top_init_inline_markup_faces>>
  <<abs_top_init_string_links_and_images>>
  <<abs_top_init_trail>>
  <<abs_inline_para_tag_associations>>
                                                                                /+ ↓ abstract marked up document +/
  @system auto docAbstraction(CMM,Opt,Mf) (
    char[][]           markup_sourcefile_content,
    CMM                conf_make_meta,
    Opt                opt_action,
    Mf                 manifested,
    bool               _new_doc
  ) {
    static auto rgx = RgxI();
                                                                                /+ ↓ abstraction init +/
    <<abs_init_rest_0>>
    <<abs_init_rest_1>>
                                                                                /+ abstraction init ↑ +/
    <<make_tests>>
                                                                                /+ ↓ ↻ loop markup document/text line by line +/
    srcDocLoop:
    foreach (line; markup_sourcefile_content) {                                 /+ ↓ markup document/text line by line +/
                                                                                // "line" variable can be empty but should never be null
      <<abs_in_loop_body_00_0>>
      <<abs_in_loop_body_00_1>>
      if ( pith["block_is"] == eN.blk_is.code
        && pith["block_state"] == eN.blk_state.on
      ) {
        <<abs_in_loop_body_00_code_block>>
      } else if (!matchFirst(line, rgx.skip_from_regular_parse)) {              /+ object other than "code block" object +/
                                                                                /+ (includes regular text paragraph, headings & blocks other than code) +/
                                                                                /+ heading, glossary, blurb, poem, group, block, quote, table +/
        <<abs_in_loop_body_non_code_obj_fontface_markup>>
        <<abs_in_loop_body_non_code_obj_in_biblio>>
        <<abs_in_loop_body_non_code_obj_in_glossary>>
        <<abs_in_loop_body_non_code_obj_in_blurb>>
        <<abs_in_loop_body_non_code_obj_in_block>>
          <<abs_in_loop_body_non_code_obj_in_block_quote>>
          <<abs_in_loop_body_non_code_obj_in_block_group>>
          <<abs_in_loop_body_non_code_obj_in_block_block>>
          <<abs_in_loop_body_non_code_obj_in_block_poem>>
          <<abs_in_loop_body_non_code_obj_in_block_table>>
        } else {                                                                /+ not within a block group +/
          <<abs_in_loop_body_open_block_obj_assert>>
          if (line.matchFirst(rgx.block_open)) {
            <<abs_in_loop_body_open_block_obj>>
          } else if (!line.empty) {                                             /+ line not empty +/
                                                                                /+ non blocks (headings, paragraphs) & closed blocks +/
            <<abs_in_loop_body_not_block_obj_assert>>
            <<abs_in_loop_body_not_block_obj_bookindex>>
            <<abs_in_loop_body_not_block_obj_not_bookindex>>
              <<abs_in_loop_body_not_block_obj_not_bookindex_a_comment>>
              <<abs_in_loop_body_not_block_obj_not_bookindex_a_para_type>>
              <<abs_in_loop_body_not_block_obj_not_bookindex_a_heading>>
              <<abs_in_loop_body_not_block_obj_not_bookindex_a_para>>
            <<abs_in_loop_body_not_block_obj_not_bookindex_close>>
          } else if (pith["block_state"] == eN.blk_state.closing) {             /+ line empty, with blocks flag +/
            <<abs_in_loop_body_not_block_obj_line_empty_blocks_flags>>
          } else {                                                              /+ line.empty, post contents, empty variables: +/
            <<abs_in_loop_body_not_block_obj_line_empty_assert>>
            <<abs_in_loop_body_not_block_obj_line_empty_heading_obj>>
            <<abs_in_loop_body_not_block_obj_line_empty_para_obj>>
          }                                                                     // close else for line empty
        }                                                                       // close else for not the above
      }                                                                         // close after non code, other blocks or regular text
      <<abs_in_loop_body_reloop_get_prior_state>>
    }                                                                           /+ ← srcDocLoop closed: loop markup document/text line by line +/
                                                                                /+ ↓ post loop markup document/text +/
                                                                                /+ ↓ post loop markup document/text +/
    <<abs_loop_body_exit_eof_xml_dom_tail>>
    <<abs_post_endnote_tuple>>
    <<abs_post_glossary_nugget>>
    <<abs_post_biblio_init>>
    <<abs_post_biblio>>
    <<abs_post_biblio_debug_write>>
    <<abs_post_bookindex>>
    <<abs_post_no_blurb>>
    <<abs_post_contents>>
    <<abs_post_separate_doc_head_and_doc_body>>
    <<abs_post_heading_ancestors_markup>>
    <<abs_post_heading_ancestors_collapsed>>
    <<abs_post_loop_section_body_get_ancestors>>
    <<abs_post_loop_section_endnotes>>
    <<abs_post_loop_section_glossary>>
    <<abs_post_loop_section_biblio>>
    <<abs_post_loop_section_bookindex>>
    <<abs_post_loop_section_blurb>>
    <<abs_post_loop_all_obj_get_heading_decendants>>
    <<abs_post_loop_all_obj_get_image_names>>
    <<abs_post_loop_all_obj_get_image_dimensions>>
    <<abs_post_loop_all_obj_get_links>>
    <<abs_post_loop_section_head>>
    <<abs_post_loop_section_toc>>
    <<abs_post_reloop_section_body>>
    <<abs_post_reloop_section_endnotes>>
    <<abs_post_reloop_section_glossary_get_numbering>>
    <<abs_post_reloop_section_biblio_get_numbering>>
    <<abs_post_reloop_section_bookindex_get_index>>
    <<abs_post_reloop_section_blurb_get_numbering>>
    <<abs_post_reloop_section_all_get_decendants>>
    <<abs_post_heading_obj_reinitialize>>
    <<abs_post_the_document>>
    <<abs_post_document_parts_keys>>
    <<abs_post_document_segnames>>
    <<abs_post_document_reinitialize>>
    <<abs_struct_doc_has>>
    <<abs_return_tuple>>
                                                                                /+ post loop markup document/text ↑ +/
  }                                                                             /+ ← closed: abstract doc source +/
                                                                                /+ ↓ abstraction functions +/
  <<abs_functions_object_reset>>
  <<abs_functions_header_set_common>>
  <<abs_functions_ocn_status>>
  <<abs_functions_substitutions_user_requested>>
  <<abs_functions_substitutions_fontface>>
  <<abs_functions_block_open>>
    <<abs_functions_block_regex>>
    <<abs_functions_block_curly_open_code>>
    <<abs_functions_block_curly_open_poem>>
    <<abs_functions_block_curly_open_group>>
    <<abs_functions_block_curly_open_block>>
    <<abs_functions_block_curly_open_quote>>
    <<abs_functions_block_curly_open_table>>
    <<abs_functions_block_curly_open_table_special>>
    <<abs_functions_block_tic_open_code>>
    <<abs_functions_block_tic_open_poem>>
    <<abs_functions_block_tic_open_group>>
    <<abs_functions_block_tic_open_block>>
    <<abs_functions_block_tic_open_quote>>
    <<abs_functions_block_tic_open_table>>
  <<abs_functions_block_close>>
  <<abs_functions_block_quote>>
  <<abs_functions_block_group>>
  <<abs_functions_block_block>>
  <<abs_functions_block_poem>>
  <<abs_functions_block_code>>
  <<abs_functions_block_table>>
  <<abs_functions_block_biblio_map_tags>>
  <<abs_functions_block_biblio_text_block>>
  <<abs_functions_block_line_status_empty_table_closed>>
  <<abs_functions_block_line_status_empty_block_close_function_open>>
      <<abs_functions_block_line_status_empty_block_quote>>
      <<abs_functions_block_line_status_empty_block_group>>
      <<abs_functions_block_line_status_empty_block_block>>
      <<abs_functions_block_line_status_empty_block_poem>>
      <<abs_functions_block_line_status_empty_block_code>>
      <<abs_functions_block_line_status_empty_block_table>>
  <<abs_functions_block_line_status_empty_block_close_function_close>>
  <<abs_functions_book_index>>
  <<abs_functions_heading_found>>
  <<abs_functions_heading_make_set>>
  <<abs_functions_heading_matched>>
  <<abs_functions_para_matched>>
  <<abs_functions_para_font_faces_line>>
  <<abs_functions_table_instructions>>
  <<abs_functions_table_munge_array>>
  <<abs_functions_table_munge_substantive>>
  <<abs_functions_table_munge_substantive_special>>
                                                                                /+ abstraction functions ↑ +/
                                                                                /+ ↓ abstraction function emitters +/
  <<meta_emitters_ocn>>
                                                                                /+ +/
  <<meta_emitters_obj_inline_markup_munge_function_open>>
    <<meta_emitters_obj_inline_markup_munge_function_markup_images>>
    <<meta_emitters_obj_inline_markup_munge_function_markup_footnotes_endnotes>>
    <<meta_emitters_obj_inline_markup_munge_function_object_notes_and_links>>
    <<meta_emitters_obj_inline_markup_munge_function_heading>>
    <<meta_emitters_obj_inline_markup_munge_function_para>>
    <<meta_emitters_obj_inline_markup_munge_function_quote>>
    <<meta_emitters_obj_inline_markup_munge_function_group>>
    <<meta_emitters_obj_inline_markup_munge_function_block>>
    <<meta_emitters_obj_inline_markup_munge_function_verse>>
    <<meta_emitters_obj_inline_markup_munge_function_code>>
    <<meta_emitters_obj_inline_markup_munge_function_table>>
    <<meta_emitters_obj_inline_markup_munge_function_comment>>
  <<meta_emitters_obj_inline_markup_munge_function_close>>
  <<meta_emitters_obj_inline_markup_function_open>>
    <<meta_emitters_obj_inline_markup_and_anchor_tags_and_misc>>
    <<meta_emitters_obj_inline_markup_table_of_contents>>
  <<meta_emitters_obj_inline_markup_private_function_open>>
    <<meta_emitters_obj_inline_markup_heading_numbering_and_segment_anchor_tags>>
    <<meta_emitters_obj_inline_segment_anchor_tags_manufacture>>
  <<meta_emitters_obj_inline_markup_private_function_close>>
                                                                                /+ +/
  <<meta_emitters_obj_attributes>>
    <<meta_emitters_obj_attributes_public>>
    <<meta_emitters_obj_attributes_private>>
    <<meta_emitters_obj_attributes_private_an_attribute_txt_para_and_blocks>>
    <<meta_emitters_obj_attributes_private_an_attribute_txt_heading>>
    <<meta_emitters_obj_attributes_private_an_attribute_txt_para>>
    <<meta_emitters_obj_attributes_private_an_attribute_txt_quote>>
    <<meta_emitters_obj_attributes_private_an_attribute_txt_group>>
    <<meta_emitters_obj_attributes_private_an_attribute_txt_block>>
    <<meta_emitters_obj_attributes_private_an_attribute_txt_verse>>
    <<meta_emitters_obj_attributes_private_an_attribute_txt_code>>
    <<meta_emitters_obj_attributes_private_an_attribute_txt_table>>
    <<meta_emitters_obj_attributes_private_an_attribute_txt_comment>>
    <<meta_emitters_obj_attributes_private_json>>
  <<meta_emitters_obj_attributes_private_close>>
                                                                                /+ +/
  <<meta_emitters_bookindex_nugget>>
  <<meta_emitters_bookindex_report_indented>>
  <<meta_emitters_bookindex_report_section_function_open>>
    <<meta_emitters_bookindex_report_section_function_write_section>>
    <<meta_emitters_bookindex_report_section_function_build_abstraction>>
  <<meta_emitters_bookindex_report_section_function_close>>
                                                                                /+ +/
  <<meta_emitters_endnotes_function_open>>
    <<meta_emitters_endnotes_function_gather_notes>>
    <<meta_emitters_endnotes_function_gathered_notes>>
    <<meta_emitters_endnotes_function_endnote_objects>>
  <<meta_emitters_endnotes_function_close>>
                                                                                /+ +/
  <<meta_emitters_bibliography_function_open>>
    <<meta_emitters_bibliography_function_biblio_sorted>>
    <<meta_emitters_bibliography_function_biblio_unsorted_json_object_array>>
    <<meta_emitters_bibliography_function_biblio_sort_json>>
    <<meta_emitters_bibliography_function_biblio_sorted_json>>
  <<meta_emitters_bibliography_function_close>>
                                                                                /+ +/
  <<meta_emitters_metadata_function_open>>
    <<meta_emitters_metadata_function_node_location>>
    <<meta_emitters_metadata_function_heading>>
  <<meta_emitters_metadata_function_close>>
                                                                                /+ abstraction functions emitters ↑ +/
                                                                                /+ ↓ abstraction functions assertions +/
  <<abs_functions_assertions>>
                                                                                /+ abstraction functions assertions ↑ +/
}                                                                               /+ ← closed: template docAbstraction +/
<<template_doc_sect_keys_seq>>
#+END_SRC

** 1. _pre loop process ing_ :pre:
*** imports :imports:

#+NAME: abs_top_imports
#+BEGIN_SRC d
import
  std.algorithm,
  std.container,
  std.file,
  std.json,
  std.path;
import
  doc_reform.meta,
  doc_reform.meta.defaults,
  doc_reform.meta.rgx,
  doc_reform.meta.metadoc_object_setter,
  doc_reform.meta.rgx;
#+END_SRC

*** mixins :mixins:

#+NAME: abs_top_mixins
#+BEGIN_SRC d
mixin ObjectSetter;
mixin InternalMarkup;
mixin spineRgxIn;
#+END_SRC

*** initialize :initialize:
**** declare enum

#+NAME: abs_top_init_struct_enum
#+BEGIN_SRC d
@safe static auto eN() {
  struct _e {
    enum bi {
      off,
      on,
    }
    enum ocn {
      off,
      on,
      closing,
      bkidx,
      reset,
    }
    enum sect {
      unset,
      head,
      toc,
      substantive,
      bibliography,
      glossary,
      book_index,
      blurb,
    }
    enum txt_is {
      off,
      para,
      heading,
    }
    enum blk_is {
      off,
      code,
      poem,
      block,
      group,
      table,
      quote,
    }
    enum blk_state {
      off,
      on,
      closing,
    }
    enum blk_delim {
      off,
      curly,
      tic,
      curly_special,
      tic_special,
    }
  }
  return _e();
}
#+END_SRC

**** initialize general

#+NAME: abs_top_init_struct_general
#+BEGIN_SRC d
/+ initialize +/
ObjGenericComposite[] the_document_toc_section, the_document_head_section, the_document_body_section, the_document_bibliography_section, the_document_glossary_section, the_document_blurb_section, the_document_xml_dom_tail_section;
string[string] an_object, processing;
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 +/
enum DocStructMarkupHeading {
  h_sect_A,
  h_sect_B,
  h_sect_C,
  h_sect_D,
  h_text_1,
  h_text_2,
  h_text_3,
  h_text_4,
  h_text_5, // extra level, drop
  content_non_header
} // header section A-D; header text 1-4
enum Status { off, on, }
enum OCNtype { ocn, non, bkidx, }
/+ 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";
static auto obj_im = ObjInlineMarkup();
static auto obj_att = ObjAttributes();
/+ ocn +/
struct OCNset {
  int digit;
  int object_number;
  bool off;
  string identifier;
  int bkidx;
  int type;
}
OCNset obj_cite_digits;
int obj_cite_digit_, obj_cite_digit_off, obj_cite_digit_bkidx, obj_cite_digit_type;
auto object_citation_number = OCNemitter();
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,];
alias TxtPlusHasFootnotes = Tuple!(
  string, "obj_txt",
  bool,   "has_notes_reg",
  bool,   "has_notes_star",
  bool,   "has_notes_plus",
);
alias TxtPlusHasFootnotesUrlsImages = Tuple!(
  string, "obj_txt",
  bool,   "has_notes_reg",
  bool,   "has_notes_star",
  bool,   "has_notes_plus",
  bool,   "has_urls",
  bool,   "has_images_without_dimensions",
);
alias TxtAndAnchorTagPlusHasFootnotesUrlsImages = Tuple!(
   string, "obj_txt",
   string, "anchor_tag",
   bool,   "has_notes_reg",
   bool,   "has_notes_star",
   bool,   "has_links",
   bool,   "has_images_without_dimensions",
);
enum DomTags { none, open, close, close_and_open, open_still, }
#+END_SRC

**** method heading ancestors

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

**** method dom markup tags

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

**** method dom collapsed tags

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

**** method ocn emit

#+NAME: abs_top_init_ocn_emit
#+BEGIN_SRC d
@safe static  OCNset ocn_emit(int ocn_status_flag) {
  return object_citation_number.ocn_emitter(ocn_status_flag);
}
#+END_SRC

**** inline markup faces

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

**** string links & images

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

**** trail

#+NAME: abs_top_init_trail
#+BEGIN_SRC d
/+ book index variables +/
string book_idx_tmp;
string[][string][string] bookindex_unordered_hashes;
/+ node +/
ObjGenericComposite comp_obj_heading, comp_obj_location, comp_obj_block, comp_obj_code, comp_obj_poem_ocn, comp_obj_comment;
auto node_construct = NodeStructureMetadata();
enum sObj { content, anchor_tag, notes_reg, notes_star, links, image_no_dimensions }
#+END_SRC

*** inline para tag associations

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

*** scope

#+NAME: abs_init_rest_0
#+BEGIN_SRC d
scope(success) {
}
scope(failure) {
}
scope(exit) {
  destroy(the_document_toc_section);
  destroy(the_document_head_section);
  destroy(the_document_body_section);
  destroy(the_document_bibliography_section);
  destroy(the_document_glossary_section);
  destroy(the_document_blurb_section);
  destroy(the_document_xml_dom_tail_section);
  destroy(an_object);
  destroy(processing);
  destroy(biblio_arr_json);
  previous_length = 0;
  reset_note_numbers = true;
  lev_anchor_tag = "";
  anchor_tag = "";
  // dom_structure_collapsed_tags_status        = [ 0, 0, 0, 0, 0, 0, 0, 0, 0,];
  // dom_structure_markedup_tags_status_buffer  = [ 0, 0, 0, 0, 0, 0, 0, 0, 0,];
  // dom_structure_collapsed_tags_status_buffer = [ 0, 0, 0, 0, 0, 0, 0, 0, 0,];
}
#+END_SRC

*** init rest

#+NAME: abs_init_rest_1
#+BEGIN_SRC d
mixin spineNode;
auto node_para_int_    = node_metadata_para_int;
auto node_para_str_    = node_metadata_para_str;
ObjGenericComposite comp_obj_heading_, comp_obj_para, comp_obj_toc;
line_occur = [
  "heading"  : 0,
  "para"     : 0,
  "glossary" : 0,
  "blurb"    : 0,
];
uint[string] dochas = [
  "inline_links"      : 0,
  "inline_notes"      : 0,
  "inline_notes_star" : 0,
  "codeblock"         : 0,
  "table"             : 0,
  "block"             : 0,
  "group"             : 0,
  "poem"              : 0,
  "quote"             : 0,
  "images"            : 0,
];
uint[string] pith = [
  "ocn"                            : 1,
  "section"                        : 0,
  "txt_is"                         : 0,
  "block_is"                       : 0,
  "block_state"                    : 0,
  "block_delim"                    : 0,
  "make_headings"                  : 0,
  "dummy_heading_status"           : 0,
  "dummy_heading_multiple_objects" : 0,
  "no_ocn_multiple_objects"        : 0,
  "verse_new"                      : 0,
];
string[string] object_number_poem = [
  "start" : "",
  "end"   : ""
];
string[] lv_ancestors_txt = [ "", "", "", "", "", "", "", "", ];
int[string] lv = [
  "lv" : eN.bi.off,
  "h0" : eN.bi.off,
  "h1" : eN.bi.off,
  "h2" : eN.bi.off,
  "h3" : eN.bi.off,
  "h4" : eN.bi.off,
  "h5" : eN.bi.off,
  "h6" : eN.bi.off,
  "h7" : eN.bi.off,
  "lev_int_collapsed" : 0,
];
int[string] collapsed_lev = [
  "h0" : eN.bi.off,
  "h1" : eN.bi.off,
  "h2" : eN.bi.off,
  "h3" : eN.bi.off,
  "h4" : eN.bi.off,
  "h5" : eN.bi.off,
  "h6" : eN.bi.off,
  "h7" : eN.bi.off
];
string[string] heading_match_str = [
  "h_A": "^(none)",
  "h_B": "^(none)",
  "h_C": "^(none)",
  "h_D": "^(none)",
  "h_1": "^(none)",
  "h_2": "^(none)",
  "h_3": "^(none)",
  "h_4": "^(none)"
];
Regex!char[string] heading_match_rgx = [
  "h_A": regex(r"^(none)"),
  "h_B": regex(r"^(none)"),
  "h_C": regex(r"^(none)"),
  "h_D": regex(r"^(none)"),
  "h_1": regex(r"^(none)"),
  "h_2": regex(r"^(none)"),
  "h_3": regex(r"^(none)"),
  "h_4": regex(r"^(none)")
];
string _anchor_tag;
string toc_txt_;
an_object["glossary_nugget"]                                   = "";
an_object["blurb_nugget"]                                      = "";
comp_obj_heading_                                              = comp_obj_heading_.init;
comp_obj_heading_.metainfo.is_of_part                          = "frontmatter";
comp_obj_heading_.metainfo.is_of_section                       = "toc";
comp_obj_heading_.metainfo.is_of_type                          = "para";
comp_obj_heading_.metainfo.is_a                                = "heading";
comp_obj_heading_.text                                         = "Table of Contents";
comp_obj_heading_.metainfo.ocn                                 = 0;
comp_obj_heading_.metainfo.identifier                          = "";
comp_obj_heading_.metainfo.dummy_heading                       = false;
comp_obj_heading_.metainfo.object_number_off                   = true;
comp_obj_heading_.metainfo.object_number_type                  = 0;
comp_obj_heading_.tags.segment_anchor_tag_epub                 = "toc";
comp_obj_heading_.tags.anchor_tag_html                         = comp_obj_heading_.tags.segment_anchor_tag_epub;
comp_obj_heading_.tags.in_segment_html                         = comp_obj_heading_.tags.anchor_tag_html;
comp_obj_heading_.metainfo.heading_lev_markup                  = 4;
comp_obj_heading_.metainfo.heading_lev_collapsed               = 1;
comp_obj_heading_.metainfo.parent_ocn                          = 1;
comp_obj_heading_.metainfo.parent_lev_markup                   = 0;
comp_obj_heading_.ptr.html_segnames                            = html_segnames_ptr;
comp_obj_heading_.tags.anchor_tags                             = ["toc"];
comp_obj_heading_.metainfo.dom_structure_markedup_tags_status  = [ 1, 1, 0, 0, 1, 0, 0, 0];
comp_obj_heading_.metainfo.dom_structure_collapsed_tags_status = [ 1, 1, 1, 0, 0, 0, 0, 0];
tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.tags.segment_anchor_tag_epub;
auto toc_head                                                  = comp_obj_heading_;
html_segnames_ptr_cntr++;
the_document_toc_section = [toc_head];
static auto mkup = InlineMarkup();
static auto munge = ObjInlineMarkupMunge();
auto note_section = NotesSection();
auto bookindex_extract_hash = BookIndexNuggetHash();
string[][string] lev4_subtoc;
string[][string] segnames = ["html": ["toc"], "epub": ["toc"]];
int cnt1 = 1; int cnt2 = 1; int cnt3 = 1;
#+END_SRC

*** make tests

#+NAME: make_tests
#+BEGIN_SRC d
enum Substitute { match, markup, }
debug (substitutions) {
  writeln(__LINE__, ":", __FILE__, ": DEBUG substitutions:");
  if (!(conf_make_meta.make.headings.empty)) {
    writeln(conf_make_meta.make.headings);
  }
  if (conf_make_meta.make.substitute) {
    foreach(substitution_pair; conf_make_meta.make.substitute) {
       writeln("regex to match:       ", substitution_pair[Substitute.match]);
       writeln("substitution to make: ", substitution_pair[Substitute.markup]);
    }
  }
  if (conf_make_meta.make.bold) {
    writeln("regex to match:       ", conf_make_meta.make.bold[Substitute.match]);
    writeln("substitution to make: ", conf_make_meta.make.bold[Substitute.markup]);
  }
  if (conf_make_meta.make.emphasis) {
    writeln("regex to match:       ", conf_make_meta.make.emphasis[Substitute.match]);
    writeln("substitution to make: ", conf_make_meta.make.emphasis[Substitute.markup]);
  }
  if (conf_make_meta.make.italics) {
    writeln("regex to match:       ", conf_make_meta.make.italics[Substitute.match]);
    writeln("substitution to make: ", conf_make_meta.make.italics[Substitute.markup]);
  }
}
#+END_SRC

** 2. ↻ *LOOP* _loop: process document body_ :loop:document:
*** Loop scope :scope:

#+NAME: abs_in_loop_body_00_0
#+BEGIN_SRC d
/+ scope +/
scope(exit) {
}
scope(failure) {
  stderr.writefln(
    "%s\n%s\n%s:%s failed here:\n  line: %s",
    __MODULE__, __FUNCTION__,
    __FILE__, __LINE__,
    line,
  );
}
debug(source) {
  writeln(line);
}
debug(srclines) {
  if (!line.empty) {
    writefln(
      "* %s",
      line
    );
  }
}
#+END_SRC

*** check whether object number is on or turned off :ocn:

#+NAME: abs_in_loop_body_00_1
#+BEGIN_SRC d
if (!line.empty) {
  pith = line._check_ocn_status_(pith);
}
#+END_SRC

*** separate _code blocks_ from _other markup text_ [+5] [#A]
**** _code blocks_ :block:code:

#+NAME: abs_in_loop_body_00_code_block
#+BEGIN_SRC d
/+ block object: code +/
line.flow_txt_block_code(an_object, pith);
continue;
#+END_SRC

**** _non code objects_ (non-code blocks & regular text: by line) [+4] :non_code:
***** inline fontface markup

#+NAME: abs_in_loop_body_non_code_obj_fontface_markup
#+BEGIN_SRC d
line = line.inline_markup_faces; // by text line (rather than by text object), linebreaks in para problematic
#+END_SRC

***** in section (biblio, glossary, blurb) +(block group)+ [+1] :block:
****** in section: biblio :biblio:

#+NAME: abs_in_loop_body_non_code_obj_in_biblio
#+BEGIN_SRC d
if (line.matchFirst(rgx.heading_biblio)
|| (pith["section"] == eN.sect.bibliography
  && ((!(line.matchFirst(rgx.heading_glossary)))
  && (!(line.matchFirst(rgx.heading_blurb)))
  && (!(line.matchFirst(rgx.heading)))
  && (!(line.matchFirst(rgx.comment)))))
) {
  pith["section"] = eN.sect.bibliography;
  if (opt_action.backmatter && opt_action.section_biblio) {
    line.flow_txt_block_biblio(pith, bib_entry, biblio_entry_str_json, biblio_arr_json);
    debug(bibliobuild) {
      writeln("-  ", biblio_entry_str_json);
      writeln("-> ", biblio_arr_json.length);
    }
  }
  continue;
#+END_SRC

****** in section: glossary :glossary:

if there is a glossary section you need to:
- extract it
- create standard headings
- markup contents in standard way like regular paragraphs
  - need indentation and regular paragraph inline markup
- reconstitute the document with the glossary section following the endnotes

#+NAME: abs_in_loop_body_non_code_obj_in_glossary
#+BEGIN_SRC d
} else if (line.matchFirst(rgx.heading_glossary)
|| (pith["section"] == eN.sect.glossary
  && ((!(line.matchFirst(rgx.heading_biblio)))
  && (!(line.matchFirst(rgx.heading_blurb)))
  && (!(line.matchFirst(rgx.heading)))
  && (!(line.matchFirst(rgx.comment)))))
) {
  /+ within section (block object): glossary +/
  debug(glossary) {
    writeln(__LINE__);
    writeln(line);
  }
  pith["section"] = eN.sect.glossary;
  if (opt_action.backmatter && opt_action.section_glossary) {
    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";
    if (line.matchFirst(rgx.heading_glossary)) {
      {
        comp_obj_heading_                                = comp_obj_heading_.init;
        comp_obj_heading_.metainfo.is_of_part            = "backmatter";
        comp_obj_heading_.metainfo.is_of_section         = "glossary";
        comp_obj_heading_.metainfo.is_of_type            = "para";
        comp_obj_heading_.metainfo.is_a                  = "heading";
        comp_obj_heading_.text                           = "Glossary";
        comp_obj_heading_.metainfo.ocn                   = 0;
        comp_obj_heading_.metainfo.identifier            = "";
        comp_obj_heading_.metainfo.dummy_heading         = false;
        comp_obj_heading_.metainfo.object_number_off     = false;
        comp_obj_heading_.metainfo.object_number_type    = 0;
        comp_obj_heading_.tags.segment_anchor_tag_epub   = "_part_glossary";
        comp_obj_heading_.tags.anchor_tag_html           = comp_obj_heading_.tags.segment_anchor_tag_epub;
        comp_obj_heading_.tags.in_segment_html           = "glossary";
        comp_obj_heading_.tags.anchor_tags               = ["section_glossary"];
        comp_obj_heading_.metainfo.heading_lev_markup    = 1;
        comp_obj_heading_.metainfo.heading_lev_collapsed = 1;
        comp_obj_heading_.metainfo.parent_ocn            = 1;
        comp_obj_heading_.metainfo.parent_lev_markup     = 0;
        comp_obj_heading_.metainfo.dom_structure_markedup_tags_status  = [ 1, 1, 0, 0, 0, 0, 0, 0];
        comp_obj_heading_.metainfo.dom_structure_collapsed_tags_status = [ 1, 1, 0, 0, 0, 0, 0, 0];
        the_document_glossary_section                    ~= comp_obj_heading_;
        tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
        tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.tags.segment_anchor_tag_epub;
      }
      {
        comp_obj_heading_                                = comp_obj_heading_.init;
        comp_obj_heading_.metainfo.is_of_part            = "backmatter";
        comp_obj_heading_.metainfo.is_of_section         = "glossary";
        comp_obj_heading_.metainfo.is_of_type            = "para";
        comp_obj_heading_.metainfo.is_a                  = "heading";
        comp_obj_heading_.text                           = "Glossary";
        comp_obj_heading_.metainfo.ocn                   = 0;
        comp_obj_heading_.metainfo.identifier            = "";
        comp_obj_heading_.metainfo.dummy_heading         = true;
        comp_obj_heading_.metainfo.object_number_off     = true;
        comp_obj_heading_.metainfo.object_number_type    = 0;
        comp_obj_heading_.tags.segment_anchor_tag_epub   = "glossary";
        comp_obj_heading_.tags.anchor_tag_html           = comp_obj_heading_.tags.segment_anchor_tag_epub;
        comp_obj_heading_.tags.in_segment_html           = comp_obj_heading_.tags.anchor_tag_html;
        comp_obj_heading_.metainfo.heading_lev_markup    = 4;
        comp_obj_heading_.metainfo.heading_lev_collapsed = 2;
        comp_obj_heading_.metainfo.parent_ocn            = 1;
        comp_obj_heading_.metainfo.parent_lev_markup     = 0;
        comp_obj_heading_.metainfo.dom_structure_markedup_tags_status  = [ 1, 1, 0, 0, 1, 0, 0, 0];
        comp_obj_heading_.metainfo.dom_structure_collapsed_tags_status = [ 1, 1, 1, 0, 0, 0, 0, 0];
        comp_obj_heading_.tags.anchor_tags               = ["glossary"];
        the_document_glossary_section                    ~= comp_obj_heading_;
        tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
        tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.tags.segment_anchor_tag_epub;
      }
    } else {
      an_object = line.flow_para_match_(an_object, an_object_key, indent, bullet, pith, line_occur);
      comp_obj_para                               = comp_obj_para.init;
      comp_obj_para.metainfo.is_of_part           = "backmatter";
      comp_obj_para.metainfo.is_of_section        = "glossary";
      comp_obj_para.metainfo.is_of_type           = "para";
      comp_obj_para.metainfo.is_a                 = "glossary";
      comp_obj_para.text                          = links_and_images(line.to!string.strip).replaceFirst(rgx.para_attribs, "");
      comp_obj_para.metainfo.ocn                  = 0;
      comp_obj_para.metainfo.identifier           = "";
      comp_obj_para.metainfo.object_number_off    = true;
      comp_obj_para.metainfo.object_number_type   = 0;
      comp_obj_para.attrib.indent_hang            = indent["hang_position"];
      comp_obj_para.attrib.indent_base            = indent["base_position"];
      comp_obj_para.attrib.bullet                 = bullet;
      the_document_glossary_section               ~= comp_obj_para;
    }
    pith["ocn"] = eN.ocn.on;
  }
  continue;
#+END_SRC

****** in section: blurb :blurb:

if there is a blurb section you need to:
- extract it
- create standard headings (or use line provided in 1~ heading)
- markup contents in standard way like regular paragraphs
  - need regular paragraph inline markup
- reconstitute the document with the blurb section at the very end of the doucment

#+NAME: abs_in_loop_body_non_code_obj_in_blurb
#+BEGIN_SRC d
} else if (line.matchFirst(rgx.heading_blurb)
|| (pith["section"] == eN.sect.blurb
  && ((!(line.matchFirst(rgx.heading_glossary)))
  && (!(line.matchFirst(rgx.heading_biblio)))
  && (!(line.matchFirst(rgx.heading)))
  && (!(line.matchFirst(rgx.comment)))))
) {
  pith["section"] = eN.sect.blurb;
  debug(blurb) {
    writeln(__LINE__);
    writeln(line);
  }
  if (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";
    if (line.matchFirst(rgx.heading_blurb)) {
      {
        comp_obj_heading_                                              = comp_obj_heading_.init;
        comp_obj_heading_.metainfo.is_of_part                          = "backmatter";
        comp_obj_heading_.metainfo.is_of_section                       = "blurb";
        comp_obj_heading_.metainfo.is_of_type                          = "para";
        comp_obj_heading_.metainfo.is_a                                = "heading";
        comp_obj_heading_.text                                         = "Blurb";
        comp_obj_heading_.metainfo.ocn                                 = 0;
        comp_obj_heading_.metainfo.identifier                          = "";
        comp_obj_heading_.metainfo.dummy_heading                       = false;
        comp_obj_heading_.metainfo.object_number_off                   = false;
        comp_obj_heading_.metainfo.object_number_type                  = 0;
        comp_obj_heading_.tags.segment_anchor_tag_epub                 = "_part_blurb";
        comp_obj_heading_.tags.anchor_tag_html                         = comp_obj_heading_.tags.segment_anchor_tag_epub;
        comp_obj_heading_.tags.in_segment_html                         = "blurb";
        comp_obj_heading_.tags.anchor_tags                             = ["section_blurb"];
        comp_obj_heading_.metainfo.heading_lev_markup                  = 1;
        comp_obj_heading_.metainfo.heading_lev_collapsed               = 1;
        comp_obj_heading_.metainfo.parent_ocn                          = 1;
        comp_obj_heading_.metainfo.parent_lev_markup                   = 0;
        comp_obj_heading_.metainfo.dom_structure_markedup_tags_status  = [ 1, 1, 0, 0, 0, 0, 0, 0];
        comp_obj_heading_.metainfo.dom_structure_collapsed_tags_status = [ 1, 1, 0, 0, 0, 0, 0, 0];
        the_document_blurb_section                                     ~= comp_obj_heading_;
        tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
        tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.tags.segment_anchor_tag_epub;
      }
      {
        comp_obj_heading_                                              = comp_obj_heading_.init;
        comp_obj_heading_.metainfo.is_of_part                          = "backmatter";
        comp_obj_heading_.metainfo.is_of_section                       = "blurb";
        comp_obj_heading_.metainfo.is_of_type                          = "para";
        comp_obj_heading_.metainfo.is_a                                = "heading";
        comp_obj_heading_.text                                         = "Blurb";
        comp_obj_heading_.metainfo.ocn                                 = 0;
        comp_obj_heading_.metainfo.identifier                          = "";
        comp_obj_heading_.metainfo.dummy_heading                       = true;
        comp_obj_heading_.metainfo.object_number_off                   = true;
        comp_obj_heading_.metainfo.object_number_type                  = 0;
        comp_obj_heading_.tags.segment_anchor_tag_epub                 = "blurb";
        comp_obj_heading_.tags.anchor_tag_html                         = comp_obj_heading_.tags.segment_anchor_tag_epub;
        comp_obj_heading_.tags.in_segment_html                         = comp_obj_heading_.tags.anchor_tag_html;
        comp_obj_heading_.metainfo.heading_lev_markup                  = 4;
        comp_obj_heading_.metainfo.heading_lev_collapsed               = 2;
        comp_obj_heading_.metainfo.parent_ocn                          = 1;
        comp_obj_heading_.metainfo.parent_lev_markup                   = 0;
        comp_obj_heading_.tags.anchor_tags                             = ["blurb"];
        comp_obj_heading_.metainfo.dom_structure_markedup_tags_status  = [ 1, 1, 0, 0, 1, 0, 0, 0];
        comp_obj_heading_.metainfo.dom_structure_collapsed_tags_status = [ 1, 1, 1, 0, 0, 0, 0, 0];
        the_document_blurb_section                                     ~= comp_obj_heading_;
        tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
        tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.tags.segment_anchor_tag_epub;
      }
    } else if (line.matchFirst(rgx.headings)
    && (opt_action.backmatter && opt_action.section_blurb)) {
      comp_obj_heading_                                              = comp_obj_heading_.init;
      comp_obj_heading_.metainfo.is_of_part                          = "backmatter";
      comp_obj_heading_.metainfo.is_of_section                       = "blurb";
      comp_obj_heading_.metainfo.is_of_type                          = "para";
      comp_obj_heading_.metainfo.is_a                                = "heading";
      comp_obj_heading_.text                                         = line.to!string;
      comp_obj_heading_.metainfo.ocn                                 = 0;
      comp_obj_heading_.metainfo.identifier                          = "";
      comp_obj_heading_.metainfo.dummy_heading                       = false;
      comp_obj_heading_.metainfo.object_number_off                   = true;
      comp_obj_heading_.metainfo.object_number_type                  = 0;
      comp_obj_heading_.tags.segment_anchor_tag_epub                 = "blurb";
      comp_obj_heading_.tags.anchor_tag_html                         = comp_obj_heading_.tags.segment_anchor_tag_epub;
      comp_obj_heading_.tags.in_segment_html                         = comp_obj_heading_.tags.anchor_tag_html;
      comp_obj_heading_.metainfo.heading_lev_markup                  = an_object["lev_markup_number"].to!int;    // make int, remove need to conv
      comp_obj_heading_.metainfo.heading_lev_collapsed               = an_object["lev_collapsed_number"].to!int; // make int, remove need to conv
      comp_obj_heading_.metainfo.parent_ocn                          = 1;
      comp_obj_heading_.metainfo.parent_lev_markup                   = 0;
      the_document_blurb_section                                     ~= comp_obj_heading_;
      tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
      tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.tags.segment_anchor_tag_epub;
    } else if (!(line.empty)) {
      an_object = line.flow_para_match_(an_object, an_object_key, indent, bullet, pith, line_occur);
      comp_obj_para                               = comp_obj_para.init;
      comp_obj_para.metainfo.is_of_part           = "backmatter";
      comp_obj_para.metainfo.is_of_section        = "blurb";
      comp_obj_para.metainfo.is_of_type           = "para";
      comp_obj_para.metainfo.is_a                 = "blurb";
      comp_obj_para.text                          = links_and_images(line.to!string.strip).replaceFirst(rgx.para_attribs, "");
      comp_obj_para.metainfo.ocn                  = 0;
      comp_obj_para.metainfo.identifier           = "";
      comp_obj_para.metainfo.object_number_off    = true;
      comp_obj_para.metainfo.object_number_type   = 0;
      comp_obj_para.attrib.indent_hang            = indent["hang_position"];
      comp_obj_para.attrib.indent_base            = indent["base_position"];
      comp_obj_para.has.inline_links              = true;
      comp_obj_para.attrib.bullet                 = bullet;
      the_document_blurb_section                  ~= comp_obj_para;
    }
    pith["ocn"] = eN.ocn.on;
  }
  continue;
#+END_SRC

***** in blocks [+1] :block:

#+NAME: abs_in_loop_body_non_code_obj_in_block
#+BEGIN_SRC d
} else if (pith["block_state"] == eN.blk_state.on) {
#+END_SRC

****** in block: quote :quote:

#+NAME: abs_in_loop_body_non_code_obj_in_block_quote
#+BEGIN_SRC d
if (pith["block_is"]    == eN.blk_is.quote) {
  line = line
    ._doc_header_and_make_substitutions_(conf_make_meta)
    ._doc_header_and_make_substitutions_fontface_(conf_make_meta);
  an_object = line.flow_txt_block_quote(an_object, pith);
  continue;
#+END_SRC

****** in block: group :group:

#+NAME: abs_in_loop_body_non_code_obj_in_block_group
#+BEGIN_SRC d
} else if (pith["block_is"]    == eN.blk_is.group) {
  line = line
    ._doc_header_and_make_substitutions_(conf_make_meta)
    ._doc_header_and_make_substitutions_fontface_(conf_make_meta)
    .replaceAll(rgx.para_delimiter, mkup.br_line_spaced ~ "$1");
  an_object = line.flow_txt_block_group(an_object, pith);
  continue;
#+END_SRC

****** in block: block :block:

#+NAME: abs_in_loop_body_non_code_obj_in_block_block
#+BEGIN_SRC d
} else if (pith["block_is"]    == eN.blk_is.block) {
  line = line
    ._doc_header_and_make_substitutions_(conf_make_meta)
    ._doc_header_and_make_substitutions_fontface_(conf_make_meta);
  if (auto m = line.match(rgx.spaces_keep)) {
    line = line
      .replaceAll(rgx.spaces_keep, (m.captures[1]).translate([ ' ' : mkup.nbsp ]));
  }
  an_object = line.flow_txt_block_block(an_object, pith);
  continue;
#+END_SRC

****** in block: poem :poem:

#+NAME: abs_in_loop_body_non_code_obj_in_block_poem
#+BEGIN_SRC d
} else if (pith["block_is"]    == eN.blk_is.poem) {
  an_object = line.flow_txt_block_poem(an_object, pith, cntr, object_number_poem, conf_make_meta, tag_in_seg);
  continue;
#+END_SRC

****** in block: table :table:

#+NAME: abs_in_loop_body_non_code_obj_in_block_table
#+BEGIN_SRC d
} else if (pith["block_is"]    == eN.blk_is.table) {
  an_object = line.flow_txt_block_table(an_object, pith, conf_make_meta);
  continue;
}
#+END_SRC

***** not identified as being within block group (could still be, or not) [+3]
****** assert

#+NAME: abs_in_loop_body_open_block_obj_assert
#+BEGIN_SRC d
assert(
  (pith["block_state"] == eN.blk_state.off)
  || (pith["block_state"] == eN.blk_state.closing),
  "block status: none or closed"
);
#+END_SRC

****** catch misc +block open+

#+NAME: abs_in_loop_body_open_block_obj
#+BEGIN_SRC d
if (line.matchFirst(rgx.block_poem_open)) {                                    /+ poem to verse exceptions! +/
  object_reset(an_object);
  processing.remove("verse");
  object_number_poem["start"] = obj_cite_digits.object_number.to!string;
}
line.flow_txt_block_start(pith, dochas, object_number_poem);
continue;
#+END_SRC

****** line not empty [+2]
******* asserts :assert:

#+NAME: abs_in_loop_body_not_block_obj_assert
#+BEGIN_SRC d
assert(
  !line.empty,
  "line tested, line not empty surely:\n  \"" ~ line ~ "\""
);
assert(
  (pith["block_state"] == eN.blk_state.off)
  || (pith["block_state"] == eN.blk_state.closing),
  "code block status: none or closed"
);
if (pith["block_state"] == eN.blk_state.closing) {
  debug(check) {
    writeln(__LINE__);
    writeln(line);
  }
  assert(
    line.matchFirst(rgx.book_index_item)
    || line.matchFirst(rgx.book_index_item_open)
    || pith["section"] == eN.sect.book_index,
    "\nblocks closed, unless followed by book index, non-matching line:\n  \""
    ~ line ~ "\""
  );
}
#+END_SRC

******* book index :bookindex:

#+NAME: abs_in_loop_body_not_block_obj_bookindex
#+BEGIN_SRC d
if (line.matchFirst(rgx.book_index_item)
|| line.matchFirst(rgx.book_index_item_open)
|| pith["section"] == eN.sect.book_index)  {                            /+ book_index +/
  an_object = line.flow_book_index_(an_object, book_idx_tmp, pith, opt_action);
#+END_SRC

******* not book index [+1]

#+NAME: abs_in_loop_body_not_block_obj_not_bookindex
#+BEGIN_SRC d
} else {                                                                       /+ not book_index +/
#+END_SRC

******** matched: comment :comment:match:

#+NAME: abs_in_loop_body_not_block_obj_not_bookindex_a_comment
#+BEGIN_SRC d
an_object_key = "body_nugget";
if (auto m = line.matchFirst(rgx.comment)) {                                 /+ matched comment +/
  debug(comment) {
    writeln(line);
  }
  an_object[an_object_key]                ~= line ~= "\n";
  comp_obj_comment                        = comp_obj_comment.init;
  comp_obj_comment.metainfo.is_of_part    = "comment"; // breaks flow
  comp_obj_comment.metainfo.is_of_section = "comment"; // breaks flow
  comp_obj_comment.metainfo.is_of_type    = "comment";
  comp_obj_comment.metainfo.is_a          = "comment";
  comp_obj_comment.text                   = an_object[an_object_key].strip;
  the_document_body_section               ~= comp_obj_comment;
  flow_common_reset_(line_occur, an_object, pith);
  processing.remove("verse");
  ++cntr;
#+END_SRC

******** flag !set & line !exist: heading or para :heading:paragraph:

#+NAME: abs_in_loop_body_not_block_obj_not_bookindex_a_para_type
#+BEGIN_SRC d
} else if ((line_occur["para"] == eN.bi.off
  && line_occur["heading"] == eN.bi.off)
  && pith["txt_is"] == eN.txt_is.off
) {                             /+ heading or para but neither flag nor line exists +/
  if ((conf_make_meta.make.headings.length > 2)
  && (pith["make_headings"] == eN.bi.off)) {                      /+ heading found +/
    heading_match_str = line.flow_heading_found_(heading_match_str, conf_make_meta.make.headings, heading_match_rgx, 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 +/
    line = line.flow_heading_make_set_(line_occur, heading_match_rgx, pith);
  }
  /+ TODO node info: all headings identified at this point,
     - extract node info here??
     - how long can it wait?
     - should be incorporated in composite objects
     - should happen before endnote links set (they need to be moved down?)
  +/
  if (line.matchFirst(rgx.headings)) {                                        /+ heading match +/
    line = line._doc_header_and_make_substitutions_(conf_make_meta);
    an_object = line.flow_heading_matched_(
      an_object,
      line_occur,
      an_object_key,
      lv,
      collapsed_lev,
      pith,
      conf_make_meta,
    );
  } else if (line_occur["para"] == eN.bi.off) {                              /+ para match +/
    an_object_key = "body_nugget";
    line = line
      ._doc_header_and_make_substitutions_(conf_make_meta)
      ._doc_header_and_make_substitutions_fontface_(conf_make_meta);
    an_object = line.flow_para_match_(an_object, an_object_key, indent, bullet, pith, line_occur);
  }
#+END_SRC

******** line exist: heading :heading:

#+NAME: abs_in_loop_body_not_block_obj_not_bookindex_a_heading
#+BEGIN_SRC d
} else if (line_occur["heading"] > eN.bi.off) {                              /+ heading +/
  debug(heading) {
    writeln(line);
  }
  an_object[an_object_key] ~= line ~= "\n";
  ++line_occur["heading"];
#+END_SRC

******** line exist: para :para:

#+NAME: abs_in_loop_body_not_block_obj_not_bookindex_a_para
#+BEGIN_SRC d
} else if (line_occur["para"] > eN.bi.off) {                                 /+ paragraph +/
  debug(para) {
    writeln(an_object_key, "-> ", line);
  }
  line = line
    ._doc_header_and_make_substitutions_(conf_make_meta)
    ._doc_header_and_make_substitutions_fontface_(conf_make_meta);
  an_object[an_object_key] ~= " " ~ line;
  ++line_occur["para"];
}
#+END_SRC

******** not book index close

#+NAME: abs_in_loop_body_not_block_obj_not_bookindex_close
#+BEGIN_SRC d
}
#+END_SRC

****** line empty, with block flag

#+NAME: abs_in_loop_body_not_block_obj_line_empty_blocks_flags
#+BEGIN_SRC d
an_object = line.flow_block_flag_line_empty_(
  an_object,
  bookindex_extract_hash,
  the_document_body_section,
  bookindex_unordered_hashes,
  obj_cite_digits,
  comp_obj_heading,
  cntr,
  pith,
  object_number_poem,
  conf_make_meta,
  tag_in_seg,
);
#+END_SRC

****** line empty [+1]
******* assert line empty :assert:

#+NAME: abs_in_loop_body_not_block_obj_line_empty_assert
#+BEGIN_SRC d
assert(
  line.empty,
  "\nline should be empty:\n  \""
  ~ line ~ "\""
);
assert(
  (pith["block_state"] == eN.blk_state.off),
  "code block status: none"
);
#+END_SRC

******* heading object :heading:object:

#+NAME: abs_in_loop_body_not_block_obj_line_empty_heading_obj
#+BEGIN_SRC d
if (_new_doc) {
  tag_assoc   = tag_assoc.init;
  lv0to3_tags = lv0to3_tags.init;
  tag_in_seg  = tag_in_seg.init;
}
if (pith["txt_is"] == eN.txt_is.heading
  && line_occur["heading"] > eN.bi.off
) {                                      /+ heading object (current line empty) +/
  obj_cite_digits = (an_object["lev_markup_number"].to!int == 0)
  ? ocn_emit(eN.ocn.reset)
  : ocn_emit(pith["ocn"]);
  an_object["is"] = "heading";
  an_object_key = "body_nugget";
  TxtAndAnchorTagPlusHasFootnotesUrlsImages substantive_object_and_anchor_tags_tuple
    = obj_im.obj_inline_markup_and_anchor_tags_and_misc(an_object, an_object_key, conf_make_meta, ((_new_doc) ? Yes._new_doc : No._new_doc));
  an_object["substantive"] = substantive_object_and_anchor_tags_tuple[sObj.content];
  anchor_tag = substantive_object_and_anchor_tags_tuple[sObj.anchor_tag];
  if (_new_doc) {
    cnt1 = 1;
    cnt2 = 1;
    cnt3 = 1;
    _new_doc = false;
  }
  if (
    an_object["lev_markup_number"].to!int == 4
    && (!(anchor_tag.empty)
    || (lv0to3_tags.length > 0))
  ) {
    tag_in_seg["seg_lv4"]    = anchor_tag;
    tag_in_seg["seg_lv1to4"] = anchor_tag;
    lev_anchor_tag = anchor_tag;
    tag_assoc[anchor_tag]["seg_lv4"]    = tag_in_seg["seg_lv4"];
    tag_assoc[anchor_tag]["seg_lv1to4"] = tag_in_seg["seg_lv1to4"];
    if (lv0to3_tags.length > 0) { /+ names used for html markup segments 1 to 4 (rather than epub which has separate segments for A to D) +/
      foreach (lv0_to_lv3_html_tag; lv0to3_tags) {
        tag_assoc[lv0_to_lv3_html_tag]["seg_lv4"] = anchor_tag;
      }
    }
    anchor_tag_ = anchor_tag;
    lv0to3_tags = lv0to3_tags.init;
  } else if (an_object["lev_markup_number"].to!int > 4) {
    tag_in_seg["seg_lv4"]    = anchor_tag_;
    tag_in_seg["seg_lv1to4"] = anchor_tag_;
    lev_anchor_tag           = anchor_tag;
    tag_assoc[anchor_tag]["seg_lv4"] = tag_in_seg["seg_lv4"];
    tag_assoc[anchor_tag]["seg_lv1to4"] = tag_in_seg["seg_lv1to4"];
  } else if (an_object["lev_markup_number"].to!int < 4) {
    string segn;
    switch (an_object["lev_markup_number"].to!int) {                           /+ names used for epub markup segments A to D +/
    case 0:
      segn = "_the_title";
      goto default;
    case 1:
      segn = "_part_" ~ cnt1.to!string;
      ++cnt1;
      goto default;
    case 2:
      segn = "_part_" ~ cnt1.to!string ~ "_" ~ cnt2.to!string;
      ++cnt2;
      goto default;
    case 3:
      segn =  "_part_" ~ cnt1.to!string ~ "_" ~ cnt2.to!string ~ "_" ~ cnt3.to!string;
      ++cnt3;
      goto default;
    default:
      lv0to3_tags ~= obj_cite_digits.object_number.to!string;
      lv0to3_tags ~= segn;
      tag_in_seg["seg_lv4"]    = segn; // for html segname need following lv4 not yet known
      tag_in_seg["seg_lv1to4"] = segn;
      break;
    }
  }
  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);
  /+ (incrementally build toc) table of contents here! +/
  _anchor_tag                   = obj_cite_digits.identifier;
  the_document_toc_section      = obj_im.flow_table_of_contents_gather_headings(
    an_object,
    conf_make_meta,
    tag_in_seg,
    _anchor_tag,
    lev4_subtoc,
    the_document_toc_section,
  );
  if (an_object["lev_markup_number"] == "4") {
    segnames["html"] ~= tag_in_seg["seg_lv4"];
    html_segnames_ptr = html_segnames_ptr_cntr;
    html_segnames_ptr_cntr++;
  }
  if (an_object["lev_markup_number"].to!int <= 4) {
    segnames["epub"] ~= tag_in_seg["seg_lv1to4"];
  }
  auto comp_obj_heading
    = node_construct.node_emitter_heading(
      an_object["substantive"],
      an_object["lev_markup_number"],
      an_object["lev_collapsed_number"],
      an_object["dummy_heading_status"],
      tag_in_seg,
      lev_anchor_tag,
      tag_assoc,
      obj_cite_digits,                              // OCNset
      cntr,                                         // int
      heading_ptr,                                  // int
      lv_ancestors_txt,                             // string[]
      an_object["is"],                              // string
      html_segnames_ptr,                            // int
      substantive_object_and_anchor_tags_tuple[sObj.notes_reg],
      substantive_object_and_anchor_tags_tuple[sObj.notes_star],
      substantive_object_and_anchor_tags_tuple[sObj.links],
    );
  ++heading_ptr;
  debug(segments) {
    writeln(an_object["lev_markup_number"]);
    writeln(tag_in_seg["seg_lv4"]);
    writeln(tag_in_seg["seg_lv1to4"]);
  }
  the_document_body_section ~= comp_obj_heading;
  debug(objectrelated1) { // check
    writeln(line);
  }
  flow_common_reset_(line_occur, an_object, pith);
  an_object.remove("lev");
  an_object.remove("lev_markup_number");
  processing.remove("verse");
  ++cntr;
#+END_SRC

******* paragraph object :paragraph:object:

#+NAME: abs_in_loop_body_not_block_obj_line_empty_para_obj
#+BEGIN_SRC d
} else if (pith["txt_is"] == eN.txt_is.para
  && line_occur["para"] > eN.bi.off
) {
  /+ paragraph object (current line empty) +/
  /+ repeated character paragraph separator +/
  if ((an_object[an_object_key].to!string).matchFirst(rgx.repeated_character_line_separator)) {
    pith["ocn"] = eN.ocn.off;
  }
  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"] = "para";
  auto comp_obj_heading
    = 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"],
    );
  TxtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_tuple
    = 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_tuple[sObj.content];
  anchor_tag = substantive_obj_misc_tuple[sObj.anchor_tag];
  comp_obj_para                                       = comp_obj_para.init;
  comp_obj_para.metainfo.is_of_part                   = "body";
  comp_obj_para.metainfo.is_of_section                = "body";
  comp_obj_para.metainfo.is_of_type                   = "para";
  comp_obj_para.metainfo.is_a                         = "para";
  comp_obj_para.text                                  = an_object["substantive"].to!string.strip;
  comp_obj_para.tags.html_segment_anchor_tag_is       = tag_in_seg["seg_lv4"];
  comp_obj_para.tags.epub_segment_anchor_tag_is       = tag_in_seg["seg_lv1to4"];
  comp_obj_para.metainfo.ocn                          = obj_cite_digits.object_number;
  comp_obj_para.metainfo.identifier                   = obj_cite_digits.identifier;
  comp_obj_para.metainfo.object_number_off            = (obj_cite_digits.off == 0)   ? true : false; // TODO
  comp_obj_para.metainfo.o_n_book_index               = obj_cite_digits.bkidx;
  comp_obj_para.metainfo.object_number_type           = obj_cite_digits.type;
  comp_obj_para.attrib.indent_hang                    = indent["hang_position"];
  comp_obj_para.attrib.indent_base                    = indent["base_position"];
  comp_obj_para.attrib.bullet                         = bullet;
  comp_obj_para.tags.anchor_tags                      = [anchor_tag]; anchor_tag="";
  comp_obj_para.has.inline_notes_reg                  = substantive_obj_misc_tuple[sObj.notes_reg];
  comp_obj_para.has.inline_notes_star                 = substantive_obj_misc_tuple[sObj.notes_star];
  comp_obj_para.has.inline_links                      = substantive_obj_misc_tuple[sObj.links];
  comp_obj_para.has.image_without_dimensions          = substantive_obj_misc_tuple[sObj.image_no_dimensions];
  the_document_body_section                           ~= comp_obj_para;
  tag_assoc                                           = an_object.inline_para_link_anchor(tag_in_seg, tag_assoc);
  flow_common_reset_(line_occur, an_object, pith);
  indent = [
    "hang_position" : 0,
    "base_position" : 0,
  ];
  bullet = false;
  processing.remove("verse");
  ++cntr;
} else {
  // could be useful to test line variable should be empty and never null
}
#+END_SRC

*** regular _text objects_ identified :text:paragraph:

#+NAME: abs_in_loop_body_reloop_get_prior_state
#+BEGIN_SRC d
/+ unless (the_document_body_section.length == 0) ? +/
if (the_document_body_section.length > 0) {
  if (((the_document_body_section[$-1].metainfo.is_a == "para")
    || (the_document_body_section[$-1].metainfo.is_a == "heading")
    || (the_document_body_section[$-1].metainfo.is_a == "quote")
    || (the_document_body_section[$-1].metainfo.is_a == "group")
    || (the_document_body_section[$-1].metainfo.is_a == "block")
    || (the_document_body_section[$-1].metainfo.is_a == "verse"))
  && (the_document_body_section.length > previous_length)) {
    if ((the_document_body_section[$-1].metainfo.is_a == "heading")
    && (the_document_body_section[$-1].metainfo.heading_lev_markup < 5)) {
      pith["section"] = eN.sect.unset;
    }
    if (the_document_body_section[$-1].metainfo.is_a == "verse") {             /+ scan for endnotes for whole poem (each verse in poem) +/
      foreach (i; previous_length .. the_document_body_section.length) {
        if (the_document_body_section[i].metainfo.is_a == "verse") {
          if ((the_document_body_section[i].text).match(
            rgx.inline_notes_al_all_note
          )) {
            note_section.gather_notes_for_endnote_section(
              the_document_body_section,
              tag_in_seg,
              (i).to!int,
            );
          }
        }
      }
    } else {                                                                   /+ scan object for endnotes +/
      previous_length = the_document_body_section.length.to!int;
      if ((the_document_body_section[$-1].text).match(
        rgx.inline_notes_al_all_note
      )) {
        previous_count = (the_document_body_section.length -1).to!int;
        note_section.gather_notes_for_endnote_section(
          the_document_body_section,
          tag_in_seg,
          (the_document_body_section.length-1).to!int,
        );
      }
    }
    previous_length = the_document_body_section.length.to!int;
  }
}
#+END_SRC

** 3. _post main-loop processing_ :post:
*** misc

/+
  Backmatter:
  - endnotes
  - glossary
  - bibliography / references
  - book index
  - blurb
+/

*** tie up preparation of document sections
**** in section: xml_dom_tail_section

eof xml_dom_tail_section

#+NAME: abs_loop_body_exit_eof_xml_dom_tail
#+BEGIN_SRC d
{ // EOF
  comp_obj_heading_                                              = comp_obj_heading_.init;
  comp_obj_heading_.metainfo.is_of_part                          = "backmatter";
  comp_obj_heading_.metainfo.is_of_section                       = "tail";
  comp_obj_heading_.metainfo.is_of_type                          = "para";
  comp_obj_heading_.metainfo.is_a                                = "heading";
  comp_obj_heading_.text                                         = "";
  comp_obj_heading_.metainfo.ocn                                 = 0;
  comp_obj_heading_.metainfo.identifier                          = "";
  comp_obj_heading_.metainfo.dummy_heading                       = false;
  comp_obj_heading_.metainfo.object_number_off                   = false;
  comp_obj_heading_.metainfo.object_number_type                  = 0;
  comp_obj_heading_.tags.segment_anchor_tag_epub                 = "_part_eof";
  comp_obj_heading_.tags.anchor_tag_html                         = comp_obj_heading_.tags.segment_anchor_tag_epub;
  comp_obj_heading_.tags.in_segment_html                         = "tail";
  comp_obj_heading_.tags.anchor_tags                             = ["section_eof"];
  comp_obj_heading_.metainfo.heading_lev_markup                  = 1;
  comp_obj_heading_.metainfo.heading_lev_collapsed               = 1;
  comp_obj_heading_.metainfo.parent_ocn                          = 1;
  comp_obj_heading_.metainfo.parent_lev_markup                   = 0;
  comp_obj_heading_.metainfo.dom_structure_markedup_tags_status  = [ 0, 0, 0, 0, 0, 0, 0, 0];
  comp_obj_heading_.metainfo.dom_structure_collapsed_tags_status = [ 0, 0, 0, 0, 0, 0, 0, 0];
  the_document_xml_dom_tail_section                              ~= comp_obj_heading_;
  tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
  tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.tags.segment_anchor_tag_epub;
}
#+END_SRC

**** endnotes section (scroll & seg) :endnotes:

#+NAME: abs_post_endnote_tuple
#+BEGIN_SRC d
auto en_tuple = note_section.endnote_objects(obj_cite_digits, opt_action);
auto the_document_endnotes_section = en_tuple[0];
obj_cite_digits = en_tuple[1];
debug(endnotes) {
  writefln("%s %s",
    __LINE__,
    the_document_endnotes_section.length
  );
  foreach (o; the_document_endnotes_section) {
    writeln(o);
  }
}
#+END_SRC

**** no glossary section? :glossary:

#+NAME: abs_post_glossary_nugget
#+BEGIN_SRC d
if (an_object["glossary_nugget"].length == 0) {
  comp_obj_heading_                                   = comp_obj_heading_.init;
  comp_obj_heading_.metainfo.is_of_part               = "empty";
  comp_obj_heading_.metainfo.is_of_section            = "empty";
  comp_obj_heading_.metainfo.is_of_type               = "para";
  comp_obj_heading_.metainfo.is_a                     = "heading";
  comp_obj_heading_.text                              = "(skip) there is no Glossary section";
  comp_obj_heading_.metainfo.ocn                      = 0;
  comp_obj_heading_.metainfo.identifier               = "";
  comp_obj_heading_.metainfo.dummy_heading            = true;
  comp_obj_heading_.metainfo.object_number_off        = true;
  comp_obj_heading_.metainfo.object_number_type       = 0;
  comp_obj_heading_.metainfo.heading_lev_markup       = 1;
  comp_obj_heading_.metainfo.heading_lev_collapsed    = 1;
  comp_obj_heading_.metainfo.parent_ocn               = 1;
  comp_obj_heading_.metainfo.parent_lev_markup        = 0;
  the_document_glossary_section                       ~= comp_obj_heading_;
}
debug(glossary) {
  foreach (gloss; the_document_glossary_section) {
    writeln(gloss.text);
  }
}
#+END_SRC

**** bibliography section (objects) :bibliography:

#+NAME: abs_post_biblio_init
#+BEGIN_SRC d
auto biblio_unsorted_incomplete = biblio_arr_json.dup;
auto biblio = Bibliography();
auto biblio_ordered
  = biblio.flow_bibliography_(biblio_unsorted_incomplete, bib_arr_json);
#+END_SRC

#+NAME: abs_post_biblio
#+BEGIN_SRC d
if (biblio_ordered.length > 0) {
  {
    comp_obj_heading_                                 = comp_obj_heading_.init;
    comp_obj_heading_.metainfo.is_of_part             = "backmatter";
    comp_obj_heading_.metainfo.is_of_section          = "bibliography";
    comp_obj_heading_.metainfo.is_of_type             = "para";
    comp_obj_heading_.metainfo.is_a                   = "heading";
    comp_obj_heading_.text                            = "Bibliography";
    comp_obj_heading_.metainfo.ocn                    = 0;
    comp_obj_heading_.metainfo.identifier             = "";
    comp_obj_heading_.metainfo.dummy_heading          = false;
    comp_obj_heading_.metainfo.object_number_off      = false;
    comp_obj_heading_.metainfo.object_number_type     = 0;
    comp_obj_heading_.tags.segment_anchor_tag_epub    = "_part_bibliography";
    comp_obj_heading_.tags.anchor_tag_html            = comp_obj_heading_.tags.segment_anchor_tag_epub;
    comp_obj_heading_.tags.in_segment_html            = "bibliography";
    comp_obj_heading_.tags.anchor_tags                = ["section_bibliography"];
    comp_obj_heading_.metainfo.heading_lev_markup     = 1;
    comp_obj_heading_.metainfo.heading_lev_collapsed  = 1;
    comp_obj_heading_.metainfo.parent_ocn             = 1;
    comp_obj_heading_.metainfo.parent_lev_markup      = 0;
    comp_obj_heading_.metainfo.dom_structure_markedup_tags_status  = [ 1, 1, 0, 0, 0, 0, 0, 0];
    comp_obj_heading_.metainfo.dom_structure_collapsed_tags_status = [ 1, 1, 0, 0, 0, 0, 0, 0];
    the_document_bibliography_section                 ~= comp_obj_heading_;
    tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
    tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.tags.segment_anchor_tag_epub;
  }
  {
    comp_obj_heading_                                 = comp_obj_heading_.init;
    comp_obj_heading_.metainfo.is_of_part             = "backmatter";
    comp_obj_heading_.metainfo.is_of_section          = "bibliography";
    comp_obj_heading_.metainfo.is_of_type             = "para";
    comp_obj_heading_.metainfo.is_a                   = "heading";
    comp_obj_heading_.text                            = "Bibliography";
    comp_obj_heading_.metainfo.ocn                    = 0;
    comp_obj_heading_.metainfo.identifier             = "";
    comp_obj_heading_.metainfo.dummy_heading          = true;
    comp_obj_heading_.metainfo.object_number_off      = true;
    comp_obj_heading_.metainfo.object_number_type     = 0;
    comp_obj_heading_.tags.segment_anchor_tag_epub    = "bibliography";
    comp_obj_heading_.tags.anchor_tag_html            = comp_obj_heading_.tags.segment_anchor_tag_epub;
    comp_obj_heading_.tags.in_segment_html            = comp_obj_heading_.tags.anchor_tag_html;
    comp_obj_heading_.metainfo.heading_lev_markup     = 4;
    comp_obj_heading_.metainfo.heading_lev_collapsed  = 2;
    comp_obj_heading_.metainfo.parent_ocn             = 1;
    comp_obj_heading_.metainfo.parent_lev_markup      = 0;
    comp_obj_heading_.metainfo.dom_structure_markedup_tags_status  = [ 1, 1, 0, 0, 1, 0, 0, 0];
    comp_obj_heading_.metainfo.dom_structure_collapsed_tags_status = [ 1, 1, 1, 0, 0, 0, 0, 0];
    comp_obj_heading_.tags.anchor_tags                = ["bibliography"];
    the_document_bibliography_section                 ~= comp_obj_heading_;
    tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
    tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.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_para                                   = comp_obj_para.init;
      comp_obj_para.metainfo.is_of_part               = "backmatter";
      comp_obj_para.metainfo.is_of_section            = "bibliography";
      comp_obj_para.metainfo.is_of_type               = "para";
      comp_obj_para.metainfo.is_a                     = "bibliography";
      comp_obj_para.text                              = out_.to!string.strip;
      comp_obj_para.metainfo.ocn                      = 0;
      comp_obj_para.metainfo.identifier               = "";
      comp_obj_para.metainfo.object_number_off        = true;
      comp_obj_para.metainfo.object_number_type       = 0;
      comp_obj_para.attrib.indent_hang                = 0;
      comp_obj_para.attrib.indent_base                = 1;
      comp_obj_para.attrib.bullet                     = bullet;
      comp_obj_para.tags.anchor_tags                  = [anchor_tag];
      the_document_bibliography_section               ~= comp_obj_para;
    }
  }
} else {
  comp_obj_heading_                                   = comp_obj_heading_.init;
  comp_obj_heading_.metainfo.is_of_part               = "empty";
  comp_obj_heading_.metainfo.is_of_section            = "empty";
  comp_obj_heading_.metainfo.is_of_type               = "para";
  comp_obj_heading_.metainfo.is_a                     = "heading";
  comp_obj_heading_.text                              = "(skip) there is no Bibliography";
  comp_obj_heading_.metainfo.ocn                      = 0;
  comp_obj_heading_.metainfo.identifier               = "";
  comp_obj_heading_.metainfo.dummy_heading            = true;
  comp_obj_heading_.metainfo.object_number_off        = true;
  comp_obj_heading_.metainfo.object_number_type       = 0;
  comp_obj_heading_.metainfo.heading_lev_markup       = 1;
  comp_obj_heading_.metainfo.heading_lev_collapsed    = 1;
  comp_obj_heading_.metainfo.parent_ocn               = 1;
  comp_obj_heading_.metainfo.parent_lev_markup        = 0;
  the_document_bibliography_section                   ~= comp_obj_heading_;
}
#+END_SRC

#+NAME: abs_post_biblio_debug_write
#+BEGIN_SRC d
debug(bibliosection) {
  foreach (o; the_document_bibliography_section) {
    writeln(o.text);
  }
}
#+END_SRC

***** bibliography components

#+BEGIN_SRC d
JSONValue biblio_entry_tags_jsonstr =  `{
  "is"                               : "",
  "sortby_deemed_author_year_title"  : "",
  "deemed_author"                    : "",
  "author_raw"                       : "",
  "author"                           : "",
  "author_arr"                       : [ "" ],
  "editor_raw"                       : "",
  "editor"                           : "",
  "editor_arr"                       : [ "" ],
  "title"                            : "",
  "subtitle"                         : "",
  "fulltitle"                        : "",
  "language"                         : "",
  "trans"                            : "",
  "src"                              : "",
  "journal"                          : "",
  "in"                               : "",
  "volume"                           : "",
  "edition"                          : "",
  "year"                             : "",
  "place"                            : "",
  "publisher"                        : "",
  "url"                              : "",
  "pages"                            : "",
  "note"                             : "",
  "short_name"                       : "",
  "id"                               : ""
}`; // is: book, article, magazine, newspaper, blog, other
#+END_SRC

**** bookindex section (scroll & seg) :book:index:

#+NAME: abs_post_bookindex
#+BEGIN_SRC d
auto bi = BookIndexReportSection();
auto bi_tuple
  = bi.bookindex_build_abstraction_section(
    bookindex_unordered_hashes,
    obj_cite_digits,
    opt_action,
  );
destroy(bookindex_unordered_hashes);
auto the_document_bookindex_section = bi_tuple[0];
obj_cite_digits = bi_tuple[1];
debug(bookindex) {
  foreach (bi_entry; the_document_bookindex_section) {
    writeln(bi_entry);
  }
}
#+END_SRC

**** no blurb section? :blurb:

#+NAME: abs_post_no_blurb
#+BEGIN_SRC d
if (an_object["blurb_nugget"].length == 0) {
  comp_obj_heading_                                   = comp_obj_heading_.init;
  comp_obj_heading_.metainfo.is_of_part               = "empty";
  comp_obj_heading_.metainfo.is_of_section            = "empty";
  comp_obj_heading_.metainfo.is_of_type               = "para";
  comp_obj_heading_.metainfo.is_a                     = "heading";
  comp_obj_heading_.text                              = "(skip) there is no Blurb section";
  comp_obj_heading_.metainfo.ocn                      = 0;
  comp_obj_heading_.metainfo.identifier               = "";
  comp_obj_para.metainfo.object_number_off            = true;
  comp_obj_para.metainfo.object_number_type           = 0;
  comp_obj_heading_.tags.segment_anchor_tag_epub      = "";
  comp_obj_heading_.tags.anchor_tag_html              = "";
  comp_obj_heading_.tags.in_segment_html              = "";
  comp_obj_heading_.metainfo.heading_lev_markup       = 1;
  comp_obj_heading_.metainfo.heading_lev_collapsed    = 1;
  comp_obj_heading_.metainfo.parent_ocn               = 1;
  comp_obj_heading_.metainfo.parent_lev_markup        = 0;
  the_document_blurb_section                          ~= comp_obj_heading_;
}
debug(blurb) {
  foreach (blurb; the_document_blurb_section) {
    writeln(blurb.text);
  }
}
#+END_SRC

**** toc backmatter, table of contents backmatter (scroll & seg) :contents:

#+NAME: abs_post_contents
#+BEGIN_SRC d
indent = [
  "hang_position" : 1,
  "base_position" : 1,
];
comp_obj_toc                                          = comp_obj_toc.init;
comp_obj_toc.metainfo.is_of_part                      = "frontmatter";
comp_obj_toc.metainfo.is_of_section                   = "toc";
comp_obj_toc.metainfo.is_of_type                      = "para";
comp_obj_toc.metainfo.is_a                            = "toc";
comp_obj_toc.metainfo.ocn                             = 0;
comp_obj_toc.metainfo.identifier                      = "";
comp_obj_toc.metainfo.object_number_off               = true;
comp_obj_toc.metainfo.object_number_type              = 0;
comp_obj_toc.attrib.indent_hang                       = indent["hang_position"];
comp_obj_toc.attrib.indent_base                       = indent["base_position"];
comp_obj_toc.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_toc.text                                   = toc_txt_.to!string.strip;
  comp_obj_toc.has.inline_links                       = true;
  the_document_toc_section                            ~= comp_obj_toc;
}
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_toc.text                                   = toc_txt_.to!string.strip;
  comp_obj_toc.has.inline_links                       = true;
  the_document_toc_section                            ~= comp_obj_toc;
}
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_toc.text                                   = toc_txt_.to!string.strip;
  comp_obj_toc.has.inline_links                       = true;
  the_document_toc_section                            ~= comp_obj_toc;
}
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_toc.text                                   = toc_txt_.to!string.strip;
  comp_obj_toc.has.inline_links                       = true;
  the_document_toc_section                            ~= comp_obj_toc;
}
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_toc.has.inline_links                       = true;
  comp_obj_toc.text                                   = toc_txt_.to!string.strip;
  the_document_toc_section                            ~= comp_obj_toc;
}
debug(toc) {
  writefln(
    "%s %s",
    __LINE__,
  );
  foreach (toc_linked_heading; the_document_toc_section) {
    writeln(mkup.indent_by_spaces_provided(toc_linked_heading.attrib.indent_hang), toc_linked_heading.text);
  }
}
#+END_SRC

**** doc head (separate document head from body, make space for toc)

#+NAME: abs_post_separate_doc_head_and_doc_body
#+BEGIN_SRC d
the_document_head_section ~= the_document_body_section[0];
the_document_body_section = the_document_body_section[1..$];
#+END_SRC

*** ↻ *LOOPs* _post main-loop loops_ :loop:post: WORKON
**** 1. ↻ _Loop as required_ (e.g. backmatter): loop up to lev4, extract html segnames, set pointers

this extra loop is used/needed to determine pre and (in particular) next segment
for html, that is then used in a subsequent loop

NOTE there are issues attempting to do this on first pass as:
  - backmatter is created out of sequence and
  - it is not certain which are present

  - it is quite neat to have all in one place as we have here

  - could optimise a bit by
    - skipping this loop unless the html seg or epub output is selected

***** Methods
****** get ancestors markup

#+NAME: abs_post_heading_ancestors_markup
#+BEGIN_SRC d
@safe int[] _get_ancestors_markup(O)(O obj, ref 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;
}
#+END_SRC

****** get ancestors collapsed

#+NAME: abs_post_heading_ancestors_collapsed
#+BEGIN_SRC d
@safe int[] _get_ancestors_collapsed(O)(O obj, ref 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;
}
#+END_SRC

***** ↻ Loop section: document body [bd] :loop:body:

- substantive object numbers already exist
- number un-numbered non-substantive text

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

***** ↻ Loop section: endnotes [en] :loop:endnotes:

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

***** ↻ Loop section: glossary [gl] :loop:glossary:

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

***** ↻ Loop section: bibliography [bb] :loop:bibliography:

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

***** ↻ Loop section: book index [bi] :loop:bookindex:

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

***** ↻ Loop section: blurb [bl] :loop:blurb:

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

**** 2. ↻ _Loop all objects:_ encode _structural relationships_ (sections, segments, objects)

needed for DOM structure, segnames & subtoc, backmatter pointers & unique image
list

if used minimally only for DOM structure, segnames, subtoc, could optimise by
- skipping second and third pass unless the output html seg or epub is being
  made!

- this loop could conveniently be used more extensively for ancestors as well
  (though this information can be extracted earlier)

Build here:
- DOM structure
  - ancestors & decendants
    - ancestors could be determined earlier, but convenient to have here
    - descendants could be in the form of:
      - headings contained under current heading, and/or;
      - the range of objects under the current heading
- numbering
  - already given
    - substantive object numbers
    - endnote
  - provide
    - glossary
    - bibliography
    - book index
    - blurb
    - other non-substantive objects (prefix & other stuff)
  - you could also decide on a sequential object list, containing all objects
    (both substantive and non-substantive objects), in addition to ocn, which
    are for substantive/ citable objects within the document

(as needed) up to document heading 1~, lev4 html:

- during this (the third) pass all previous and next segment names are known
- next are not yet known for backmatter during the second pas

***** Methods
****** decendants

#+NAME: abs_post_loop_all_obj_get_heading_decendants
#+BEGIN_SRC d
@safe auto get_decendants()(ObjGenericComposite[] document_sections) {
  int[string] _heading_ocn_decendants;
  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_decendants[_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_decendants[_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_decendants[_ocn_open_key[_dts_lv]] = obj.metainfo.ocn - 1;
          _ocn_open_key[_dts_lv] = (obj.metainfo.ocn).to!string;
          _heading_ocn_decendants[_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_decendants["1"] = _last_ocn; // close existing o_n key
    }
  }
  Tuple!(int, int)[] pairs;
  foreach (pair; _heading_ocn_decendants.byPair) {
    pairs ~= tuple(pair[0].to!int, pair[1]);
  }
  return pairs.sort;
}
#+END_SRC

****** images: extract

#+NAME: abs_post_loop_all_obj_get_image_names
#+BEGIN_SRC d
string[] _images;
@safe string[] extract_images()(string content_block) {
  string[] images_;
  if (auto m = content_block.matchAll(rgx.image)) {
    images_ ~= m.captures[1];
  }
  return images_;
}
string[] segnames_0_to_4;
#+END_SRC

****** images: dimensions

#+NAME: abs_post_loop_all_obj_get_image_dimensions
#+BEGIN_SRC d
@system auto _image_dimensions(O,M)(O obj, M manifested) {
  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;
}
#+END_SRC

***** links: think about!!!
- move actual links to an array in object struct so they cannot be regex munged within text block
  - you may wish to exclude certain types of internal document link
    - object number links
      - toc
      - book index
    - footnotes and footnote numbers

#+NAME: abs_post_loop_all_obj_get_links
#+BEGIN_SRC d
@safe auto _links(O)(O obj) {
  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;
}
#+END_SRC

***** ↻ Loop section: head :loop:head:

#+NAME: abs_post_loop_section_head
#+BEGIN_SRC d
foreach (ref obj; the_document_head_section) {
  if (obj.metainfo.is_a == "heading") {
    debug(dom) {
      writeln(obj.text);
    }
    if (obj.metainfo.heading_lev_markup <= 4) {
      segnames_0_to_4 ~= obj.tags.segment_anchor_tag_epub;
    }
    if (obj.metainfo.heading_lev_markup == 0) {
      /+ TODO second hit (of two) with same assertion failure, check, fix and reinstate
      assert( obj.metainfo.ocn == 1,
        "Title OCN should be 1 not: " ~ obj.metainfo.ocn.to!string); // bug introduced 0.18.1
      +/
      obj.metainfo.ocn = 1;
      obj.metainfo.identifier = "1";
      obj.metainfo.object_number_type = OCNtype.ocn;
    }
    /+ dom structure (marked up & collapsed) +/
    if (opt_action.meta_processing_xml_dom) {
      obj = obj.obj_dom_structure_set_markup_tags(dom_structure_markedup_tags_status, obj.metainfo.heading_lev_markup);
      obj = obj.obj_dom_set_collapsed_tags(dom_structure_collapsed_tags_status, obj.metainfo.heading_lev_collapsed);
    }
    obj = obj.obj_heading_ancestors(lv_ancestors_txt);
  }
  obj = _links(obj);
}
#+END_SRC

***** ↻ Loop section: toc :loop:table_of_contents:

#+NAME: abs_post_loop_section_toc
#+BEGIN_SRC d
if (the_document_toc_section.length > 1) {
  /+ scroll +/
  dom_structure_markedup_tags_status_buffer = dom_structure_markedup_tags_status.dup;
  dom_structure_collapsed_tags_status_buffer = dom_structure_collapsed_tags_status.dup;
  foreach (ref obj; the_document_toc_section) {
    if (obj.metainfo.is_a == "heading") {
      if (obj.metainfo.heading_lev_markup <= 4) {
        segnames_0_to_4 ~= obj.tags.segment_anchor_tag_epub;
        if (obj.metainfo.heading_lev_markup == 4) {
          obj.tags.segname_next = segnames["html"][obj.ptr.html_segnames + 1];
          assert(obj.tags.anchor_tag_html == segnames["html"][obj.ptr.html_segnames],
            obj.tags.anchor_tag_html ~ "!=" ~ segnames["html"][obj.ptr.html_segnames]);
        }
      }
      /+ dom structure (marked up & collapsed) +/
      if (opt_action.meta_processing_xml_dom) {
        obj = obj.obj_dom_structure_set_markup_tags(dom_structure_markedup_tags_status, obj.metainfo.heading_lev_markup);
        obj = obj.obj_dom_set_collapsed_tags(dom_structure_collapsed_tags_status, obj.metainfo.heading_lev_collapsed);
      }
      obj = obj.obj_heading_ancestors(lv_ancestors_txt);
    }
    obj = _links(obj);
  }
}
#+END_SRC

***** ↻ Loop section: document body [bd] :loop:body:

#+NAME: abs_post_reloop_section_body
#+BEGIN_SRC d
/+ multiple 1~ levels, loop through document body +/
if (the_document_body_section.length > 1) {
  foreach (ref obj; the_document_body_section) {
    if (!(obj.metainfo.identifier.empty)) {
      if (!(((obj.metainfo.identifier) in tag_assoc)
        && ("seg_lv4" in tag_assoc[(obj.metainfo.identifier)]))
      ) {
        tag_assoc[(obj.metainfo.identifier)]["seg_lv4"]
        = obj.tags.html_segment_anchor_tag_is;
      }
      tag_assoc[(obj.metainfo.identifier)]["seg_lv1to4"]
      = obj.tags.epub_segment_anchor_tag_is;
    }
    if (obj.metainfo.is_a == "heading") {
      debug(dom) {
        writeln(obj.text);
      }
      if (obj.metainfo.heading_lev_markup <= 4) {
        segnames_0_to_4 ~= obj.tags.segment_anchor_tag_epub;
        if (obj.metainfo.heading_lev_markup == 4) {
          obj.tags.lev4_subtoc = lev4_subtoc[obj.tags.anchor_tag_html];
          obj.tags.segname_prev = segnames["html"][obj.ptr.html_segnames - 1];
          if (segnames["html"].length > obj.ptr.html_segnames + 1) {
            obj.tags.segname_next = segnames["html"][obj.ptr.html_segnames + 1];
          }
          assert(obj.tags.anchor_tag_html == segnames["html"][obj.ptr.html_segnames],
            obj.tags.anchor_tag_html ~ "!=" ~ segnames["html"][obj.ptr.html_segnames]);
        }
      }
      /+ dom structure (marked up & collapsed) +/
      if (opt_action.meta_processing_xml_dom) {
        obj = obj.obj_dom_structure_set_markup_tags(dom_structure_markedup_tags_status, obj.metainfo.heading_lev_markup);
        obj = obj.obj_dom_set_collapsed_tags(dom_structure_collapsed_tags_status, obj.metainfo.heading_lev_collapsed);
      }
      obj = obj.obj_heading_ancestors(lv_ancestors_txt);
    } else if (obj.metainfo.is_a == "para") {
       _images ~= extract_images(obj.text);
       obj = _image_dimensions(obj, manifested);
    }
    obj = _links(obj);
  }
}
auto image_list = (_images.sort()).uniq;
#+END_SRC

***** ↻ Loop section: endnotes [en] :loop:endnotes:

- endnotes have their own number, (also use in node) and they belong to calling object

#+NAME: abs_post_reloop_section_endnotes
#+BEGIN_SRC d
/+ optional only one 1~ level +/
if (the_document_endnotes_section.length > 1) {
  dom_structure_markedup_tags_status_buffer           = dom_structure_markedup_tags_status.dup;
  dom_structure_collapsed_tags_status_buffer          = dom_structure_collapsed_tags_status.dup;
  dom_structure_markedup_tags_status                  = dom_structure_markedup_tags_status_buffer.dup;
  dom_structure_collapsed_tags_status                 = dom_structure_collapsed_tags_status_buffer.dup;
  foreach (ref obj; the_document_endnotes_section) {
    if (obj.metainfo.is_a == "heading") {
      debug(dom) {
        writeln(obj.text);
      }
      if (obj.metainfo.heading_lev_markup == 1) {
        obj_cite_digits                               = ocn_emit(eN.ocn.on);
        obj.metainfo.ocn                              = obj_cite_digits.object_number;
        obj.metainfo.identifier                       = obj_cite_digits.identifier;
      }
      if (obj.metainfo.heading_lev_markup <= 4) {
        segnames_0_to_4 ~= obj.tags.segment_anchor_tag_epub;
        if (obj.metainfo.heading_lev_markup == 4) {
          obj.tags.segname_prev = segnames["html"][obj.ptr.html_segnames - 1];
          if (segnames["html"].length > obj.ptr.html_segnames + 1) {
            obj.tags.segname_next = segnames["html"][obj.ptr.html_segnames + 1];
          }
          assert(obj.tags.anchor_tag_html == segnames["html"][obj.ptr.html_segnames],
            obj.tags.anchor_tag_html ~ "!=" ~ segnames["html"][obj.ptr.html_segnames]);
        }
      }
      /+ dom structure (marked up & collapsed) +/
      if (opt_action.meta_processing_xml_dom) {
        obj = obj.obj_dom_structure_set_markup_tags(dom_structure_markedup_tags_status, obj.metainfo.heading_lev_markup);
        obj = obj.obj_dom_set_collapsed_tags(dom_structure_collapsed_tags_status, obj.metainfo.heading_lev_collapsed);
      }
      obj = obj.obj_heading_ancestors(lv_ancestors_txt);
    }
    obj = _links(obj);
  }
}
#+END_SRC

***** ↻ reLoop section: glossary [gl] :loop:glossary:

- add glossary numbering, (also use in node) no need to show in text

#+NAME: abs_post_reloop_section_glossary_get_numbering
#+BEGIN_SRC d
/+ optional only one 1~ level +/
if (the_document_glossary_section.length > 1) {
  foreach (ref obj; the_document_glossary_section) {
    if (obj.metainfo.is_a == "heading") {
      debug(dom) {
        writeln(obj.text);
      }
      if (obj.metainfo.heading_lev_markup == 1) {
        obj_cite_digits                               = ocn_emit(eN.ocn.on);
        obj.metainfo.ocn                              = obj_cite_digits.object_number;
        obj.metainfo.identifier                       = obj_cite_digits.identifier;
      }
      if (obj.metainfo.heading_lev_markup <= 4) {
        segnames_0_to_4 ~= obj.tags.segment_anchor_tag_epub;
        if (obj.metainfo.heading_lev_markup == 4) {
          obj.tags.segname_prev = segnames["html"][obj.ptr.html_segnames - 1];
          if (segnames["html"].length > obj.ptr.html_segnames + 1) {
            obj.tags.segname_next = segnames["html"][obj.ptr.html_segnames + 1];
          }
          assert(obj.tags.anchor_tag_html == segnames["html"][obj.ptr.html_segnames],
            obj.tags.anchor_tag_html ~ "!=" ~ segnames["html"][obj.ptr.html_segnames]);
        }
      }
      /+ dom structure (marked up & collapsed) +/
      if (opt_action.meta_processing_xml_dom) {
        obj = obj.obj_dom_structure_set_markup_tags(dom_structure_markedup_tags_status, obj.metainfo.heading_lev_markup);
        obj = obj.obj_dom_set_collapsed_tags(dom_structure_collapsed_tags_status, obj.metainfo.heading_lev_collapsed);
      }
      obj = obj.obj_heading_ancestors(lv_ancestors_txt);
    } else if (obj.metainfo.is_a == "glossary" && !(obj.text.empty)) {
      obj_cite_digits         = ocn_emit(eN.ocn.on);
      obj.metainfo.ocn        = obj_cite_digits.object_number;
      obj.metainfo.identifier = obj_cite_digits.identifier;
    }
    obj = _links(obj);
  }
}
#+END_SRC

***** ↻ reLoop section: bibliography [bb] :loop:bibliography:

- add bibliography numbering, (also use in node) no need to show in text

#+NAME: abs_post_reloop_section_biblio_get_numbering
#+BEGIN_SRC d
/+ optional only one 1~ level +/
if (the_document_bibliography_section.length > 1) {
  foreach (ref obj; the_document_bibliography_section) {
    if (obj.metainfo.is_a == "heading") {
      debug(dom) {
        writeln(obj.text);
      }
      if (obj.metainfo.heading_lev_markup == 1) {
        obj_cite_digits                               = ocn_emit(eN.ocn.on);
        obj.metainfo.ocn                              = obj_cite_digits.object_number;
        obj.metainfo.identifier                       = obj_cite_digits.identifier;
      }
      if (obj.metainfo.heading_lev_markup <= 4) {
        segnames_0_to_4 ~= obj.tags.segment_anchor_tag_epub;
        if (obj.metainfo.heading_lev_markup == 4) {
          obj.tags.segname_prev = segnames["html"][obj.ptr.html_segnames - 1];
          if (segnames["html"].length > obj.ptr.html_segnames + 1) {
            obj.tags.segname_next = segnames["html"][obj.ptr.html_segnames + 1];
          }
          assert(obj.tags.anchor_tag_html == segnames["html"][obj.ptr.html_segnames],
            obj.tags.anchor_tag_html ~ "!=" ~ segnames["html"][obj.ptr.html_segnames]);
        }
      }
      /+ dom structure (marked up & collapsed) +/
      if (opt_action.meta_processing_xml_dom) {
        obj = obj.obj_dom_structure_set_markup_tags(dom_structure_markedup_tags_status, obj.metainfo.heading_lev_markup);
        obj = obj.obj_dom_set_collapsed_tags(dom_structure_collapsed_tags_status, obj.metainfo.heading_lev_collapsed);
      }
      obj = obj.obj_heading_ancestors(lv_ancestors_txt);
    } else if (obj.metainfo.is_a == "bibliography") {
      obj_cite_digits                                 = ocn_emit(eN.ocn.on);
      obj.metainfo.ocn                                = obj_cite_digits.object_number;
      obj.metainfo.identifier                         = obj_cite_digits.identifier;
    }
    obj = _links(obj);
  }
}
#+END_SRC

***** ↻ Loop section: book index (scroll, seg) [bi] :loop:bookindex:

- add book index numbering?, (also use in node) no need to show in text

#+NAME: abs_post_reloop_section_bookindex_get_index
#+BEGIN_SRC d
/+ optional only one 1~ level +/
int ocn_       = obj_cite_digits.object_number;
int ocn_bkidx_ = 0;
int ocn_bidx_;
if (the_document_bookindex_section.length > 1) {                                        /+ scroll +/
  dom_structure_markedup_tags_status_buffer = dom_structure_markedup_tags_status.dup;
  dom_structure_collapsed_tags_status_buffer = dom_structure_collapsed_tags_status.dup;
  foreach (ref obj; the_document_bookindex_section) {
    if (obj.metainfo.is_a == "heading") {
      debug(dom) {
      }
      if (obj.metainfo.heading_lev_markup <= 4) {
        segnames_0_to_4 ~= obj.tags.segment_anchor_tag_epub;
      }
      if (obj.metainfo.heading_lev_markup == 1) {
        obj_cite_digits                               = ocn_emit(eN.ocn.on);
        obj.metainfo.ocn                              = obj_cite_digits.object_number;
        obj.metainfo.identifier                       = obj_cite_digits.identifier;
      }
      if (obj.metainfo.heading_lev_markup <= 4) {
        if (obj.metainfo.heading_lev_markup == 4) {
          obj.tags.segname_prev = segnames["html"][obj.ptr.html_segnames - 1];
          if (segnames["html"].length > obj.ptr.html_segnames + 1) {
            obj.tags.segname_next = segnames["html"][obj.ptr.html_segnames + 1];
          }
          assert(obj.tags.anchor_tag_html == segnames["html"][obj.ptr.html_segnames],
            obj.tags.anchor_tag_html ~ "!=" ~ segnames["html"][obj.ptr.html_segnames]);
        }
      }
      /+ dom structure (marked up & collapsed) +/
      if (opt_action.meta_processing_xml_dom) {
        obj = obj.obj_dom_structure_set_markup_tags(dom_structure_markedup_tags_status, obj.metainfo.heading_lev_markup);
        obj = obj.obj_dom_set_collapsed_tags(dom_structure_collapsed_tags_status, obj.metainfo.heading_lev_collapsed);
      }
      obj = obj.obj_heading_ancestors(lv_ancestors_txt);
    } else if (obj.metainfo.is_a == "bookindex") {
      obj_cite_digits                                 = ocn_emit(eN.ocn.bkidx);
      obj.metainfo.ocn                                = obj_cite_digits.object_number;
      obj.metainfo.identifier                         = obj_cite_digits.identifier;
      obj.metainfo.o_n_book_index                     = obj_cite_digits.bkidx;
      obj.metainfo.object_number_type                 = OCNtype.bkidx;
    }
    obj = _links(obj);
  }
  /+ TODO assert failure, reinstate
  assert(obj_cite_digit_bkidx == ocn_bidx_
    obj_cite_digit_bkidx ~ " == ocn_" ~ ocn_ ~ "?");
  +/
}
#+END_SRC

***** ↻ Loop section: blurb [bl] :loop:blurb:

#+NAME: abs_post_reloop_section_blurb_get_numbering
#+BEGIN_SRC d
/+ optional only one 1~ level +/
if (the_document_blurb_section.length > 1) {
  foreach (ref obj; the_document_blurb_section) {
    if (obj.metainfo.is_a == "heading") {
      debug(dom) {
        writeln(obj.text);
      }
      if (obj.metainfo.heading_lev_markup == 1) {
        obj_cite_digits                               = ocn_emit(eN.ocn.on);
        obj.metainfo.ocn                              = obj_cite_digits.object_number;
        obj.metainfo.identifier                       = obj_cite_digits.identifier;
      }
      if (obj.metainfo.heading_lev_markup <= 4) {
        segnames_0_to_4 ~= obj.tags.segment_anchor_tag_epub;
        if (obj.metainfo.heading_lev_markup == 4) {
          obj.tags.segname_prev = segnames["html"][obj.ptr.html_segnames - 1];
          if (segnames["html"].length > obj.ptr.html_segnames + 1) {
            obj.tags.segname_next = segnames["html"][obj.ptr.html_segnames + 1];
          }
          assert(obj.tags.anchor_tag_html == segnames["html"][obj.ptr.html_segnames],
            obj.tags.anchor_tag_html ~ "!=" ~ segnames["html"][obj.ptr.html_segnames]);
        }
      }
      /+ dom structure (marked up & collapsed) +/
      if (opt_action.meta_processing_xml_dom) {
        obj = obj.obj_dom_structure_set_markup_tags(dom_structure_markedup_tags_status, obj.metainfo.heading_lev_markup);
        obj = obj.obj_dom_set_collapsed_tags(dom_structure_collapsed_tags_status, obj.metainfo.heading_lev_collapsed);
      }
      obj = obj.obj_heading_ancestors(lv_ancestors_txt);
    } else if (obj.metainfo.is_a == "blurb") {
      obj_cite_digits                                 = ocn_emit(eN.ocn.off);
      obj.metainfo.object_number_off                  = obj_cite_digits.off;
      obj.metainfo.object_number_type                 = OCNtype.non;
    }
    obj = _links(obj);
  }
}
#+END_SRC

***** ↻ reLoop sections: get decendants :loop:document:

#+NAME: abs_post_reloop_section_all_get_decendants
#+BEGIN_SRC d
if (the_document_body_section.length > 1) {
  auto pairs = get_decendants(
    the_document_head_section ~
    the_document_body_section ~
    the_document_endnotes_section ~
    the_document_glossary_section ~
    the_document_bibliography_section ~
    the_document_bookindex_section ~
    the_document_blurb_section ~
    the_document_xml_dom_tail_section
  );
  debug(decendants_tuple) {
    pairs = pairs.sort();
    foreach (pair; pairs) {  // (pair; pairs.sort())
      writeln(pair[0], "..", pair[1]);
    }
  }
  foreach (ref obj; the_document_head_section) {
    if (obj.metainfo.is_a == "heading") {
      foreach (pair; pairs) {
        if (obj.metainfo.ocn == pair[0]) {
          obj.metainfo.last_decendant_ocn = pair[1];
        }
      }
    }
  }
  if (the_document_body_section.length > 1) {
    foreach (ref obj; the_document_body_section) {
      if (obj.metainfo.is_a == "heading") {
        foreach (pair; pairs) {
          if (obj.metainfo.ocn == pair[0]) {
            obj.metainfo.last_decendant_ocn = pair[1];
          }
        }
      }
    }
  }
  if (the_document_endnotes_section.length > 1) {
    foreach (ref obj; the_document_endnotes_section) {
      if (obj.metainfo.is_a == "heading") {
        foreach (pair; pairs) {
          if (obj.metainfo.ocn == pair[0]) {
            obj.metainfo.last_decendant_ocn = pair[1];
          }
        }
      }
    }
  }
  if (the_document_glossary_section.length > 1) {
    foreach (ref obj; the_document_glossary_section) {
      if (obj.metainfo.is_a == "heading") {
        foreach (pair; pairs) {
          if (obj.metainfo.ocn == pair[0]) {
            obj.metainfo.last_decendant_ocn = pair[1];
          }
        }
      }
    }
  }
  if (the_document_bibliography_section.length > 1) {
    foreach (ref obj; the_document_bibliography_section) {
      if (obj.metainfo.is_a == "heading") {
        foreach (pair; pairs) {
          if (obj.metainfo.ocn == pair[0]) {
            obj.metainfo.last_decendant_ocn = pair[1];
          }
        }
      }
    }
  }
  if (the_document_bookindex_section.length > 1) {
    foreach (ref obj; the_document_bookindex_section) {
      if (obj.metainfo.is_a == "heading") {
        foreach (pair; pairs) {
          if (obj.metainfo.ocn == pair[0]) {
            obj.metainfo.last_decendant_ocn = pair[1];
          }
        }
      }
    }
  }
  if (the_document_blurb_section.length > 1) {
    foreach (ref obj; the_document_blurb_section) {
      if (obj.metainfo.is_a == "heading") {
        foreach (pair; pairs) {
          if (obj.metainfo.ocn == pair[0]) {
            obj.metainfo.last_decendant_ocn = pair[1];
          }
        }
      }
    }
  }
  if (the_document_xml_dom_tail_section.length > 1) {
    foreach (ref obj; the_document_xml_dom_tail_section) {
      if (obj.metainfo.is_a == "heading") {
        foreach (pair; pairs) {
          if (obj.metainfo.ocn == pair[0]) {
            obj.metainfo.last_decendant_ocn = pair[1];
          }
        }
      }
    }
  }
}
#+END_SRC

**** update TODO BUG?

#+NAME: abs_post_heading_obj_reinitialize
#+BEGIN_SRC d
  /+ TODO
    - note create/insert heading object sole purpose eof close all open tags
      sort out:
      - obj.metainfo.dom_structure_markedup_tags_status = dom_structure_markedup_tags_status;
      - obj.metainfo.dom_structure_collapsed_tags_status = dom_structure_collapsed_tags_status;
  +/
comp_obj_heading_                                               = comp_obj_heading_.init;
comp_obj_heading_.metainfo.is_of_part                           = "empty";
comp_obj_heading_.metainfo.is_of_section                        = "empty";
comp_obj_heading_.metainfo.is_of_type                           = "para";
comp_obj_heading_.metainfo.is_a                                 = "heading";
comp_obj_heading_.metainfo.ocn                                  = 0;
comp_obj_heading_.metainfo.identifier                           = "";
comp_obj_heading_.metainfo.dummy_heading                        = true;
comp_obj_heading_.metainfo.object_number_off                    = true;
comp_obj_heading_.metainfo.object_number_type                   = 0;
comp_obj_heading_.tags.segment_anchor_tag_epub                  = "";
comp_obj_heading_.tags.anchor_tag_html                          = "";
comp_obj_heading_.tags.in_segment_html                          = "";
comp_obj_heading_.tags.html_segment_anchor_tag_is               = "";
comp_obj_heading_.tags.epub_segment_anchor_tag_is               = "";
comp_obj_heading_.metainfo.heading_lev_markup                   = 9;
comp_obj_heading_.metainfo.heading_lev_collapsed                = 9;
comp_obj_heading_.metainfo.parent_ocn                           = 0;
comp_obj_heading_.metainfo.parent_lev_markup                    = 0;
comp_obj_heading_.metainfo.dom_structure_markedup_tags_status   = dom_structure_markedup_tags_status.dup;
comp_obj_heading_.metainfo.dom_structure_collapsed_tags_status  = dom_structure_collapsed_tags_status.dup;
comp_obj_heading_ = comp_obj_heading_.obj_dom_structure_set_markup_tags(dom_structure_markedup_tags_status, 0);
comp_obj_heading_ = comp_obj_heading_.obj_dom_set_collapsed_tags(dom_structure_collapsed_tags_status, 0);
comp_obj_heading_ = comp_obj_heading_.obj_heading_ancestors(lv_ancestors_txt);
// the_dom_tail_section                      ~= comp_obj_heading_; // remove tail for now, decide on later
#+END_SRC

** 4. _return document tuple_ :post:
*** _the document_ :document:

#+NAME: abs_post_the_document
#+BEGIN_SRC d
ObjGenericComposite[][string] document_the = [
  "head":             the_document_head_section,
  "toc":              the_document_toc_section,
  /+ substantive/body: +/
  "body":             the_document_body_section,
  /+ backmatter: +/
  "endnotes":         the_document_endnotes_section,
  "glossary":         the_document_glossary_section,
  "bibliography":     the_document_bibliography_section,
  "bookindex":        the_document_bookindex_section,
  "blurb":            the_document_blurb_section,
  /+ dom tail only +/
  "tail":             the_document_xml_dom_tail_section,
];
#+END_SRC

*** document _section keys_ sequence

#+NAME: abs_post_document_parts_keys
#+BEGIN_SRC d
string[][string] document_section_keys_sequenced = [
  "scroll": ["head", "toc", "body",],
  "seg":    ["head", "toc", "body",],
  "sql":    ["head", "body",],
  "latex":  ["head", "toc", "body",]
];
if (document_the["endnotes"].length > 1) {
  document_section_keys_sequenced["scroll"] ~= "endnotes";
  document_section_keys_sequenced["seg"]    ~= "endnotes";
  document_section_keys_sequenced["latex"]  ~= "endnotes";
}
if (document_the["glossary"].length > 1) {
  document_section_keys_sequenced["scroll"] ~= "glossary";
  document_section_keys_sequenced["seg"]    ~= "glossary";
  document_section_keys_sequenced["sql"]    ~= "glossary";
  document_section_keys_sequenced["latex"]  ~= "glossary";
}
if (document_the["bibliography"].length > 1) {
  document_section_keys_sequenced["scroll"] ~= "bibliography";
  document_section_keys_sequenced["seg"]    ~= "bibliography";
  document_section_keys_sequenced["sql"]    ~= "bibliography";
  document_section_keys_sequenced["latex"]  ~= "bibliography";
}
if (document_the["bookindex"].length > 1) {
  document_section_keys_sequenced["scroll"] ~= "bookindex";
  document_section_keys_sequenced["seg"]    ~= "bookindex";
  document_section_keys_sequenced["sql"]    ~= "bookindex";
  document_section_keys_sequenced["latex"]  ~= "bookindex";
}
if (document_the["blurb"].length > 1) {
  document_section_keys_sequenced["scroll"] ~= "blurb";
  document_section_keys_sequenced["seg"]    ~= "blurb";
  document_section_keys_sequenced["sql"]    ~= "blurb";
  document_section_keys_sequenced["latex"]  ~= "blurb";
}
if ((opt_action.html)
|| (opt_action.html_scroll)
|| (opt_action.html_seg)
|| (opt_action.epub)) {
  document_section_keys_sequenced["scroll"] ~= "tail";
  document_section_keys_sequenced["seg"]    ~= "tail";
}
#+END_SRC

*** dup

#+NAME: abs_post_document_segnames
#+BEGIN_SRC d
string[] segnames_4                 = segnames["html"].dup;
string[] segnames_lv1to4            = segnames["epub"].dup;
debug(segnames) {
  writeln("segnames_lv4:    ", segnames_4);
  writeln("segnames_lv1to4: ", segnames_lv1to4);
}
#+END_SRC

*** clean out structure

#+NAME: abs_post_document_reinitialize
#+BEGIN_SRC d
destroy(the_document_head_section);
destroy(the_document_toc_section);
destroy(the_document_body_section);
destroy(the_document_endnotes_section);
destroy(the_document_glossary_section);
destroy(the_document_bibliography_section);
destroy(the_document_bookindex_section);
destroy(the_document_blurb_section);
destroy(the_document_xml_dom_tail_section);
destroy(segnames);
destroy(bookindex_unordered_hashes);
destroy(an_object);
obj_cite_digits                             = ocn_emit(eN.ocn.reset);
biblio_arr_json                             = [];
obj_cite_digit_                             = 0;
html_segnames_ptr                           = 0;
html_segnames_ptr_cntr                      = 0;
content_non_header                          = "8";
dom_structure_markedup_tags_status          = [ 0, 0, 0, 0, 0, 0, 0, 0,];
dom_structure_markedup_tags_status_buffer   = [ 0, 0, 0, 0, 0, 0, 0, 0,];
dom_structure_collapsed_tags_status         = [ 0, 0, 0, 0, 0, 0, 0, 0,];
dom_structure_collapsed_tags_status_buffer  = [ 0, 0, 0, 0, 0, 0, 0, 0,];
lev_anchor_tag = "";
anchor_tag = "";
#+END_SRC

*** doc has struct

#+NAME: abs_struct_doc_has
#+BEGIN_SRC d
@safe auto doc_has() {
  struct DocHas_ {
    uint inline_links() {
      return dochas["inline_links"];
    }
    uint inline_notes_reg() {
      return dochas["inline_notes"];
    }
    uint inline_notes_star() {
      return dochas["inline_notes_star"];
    }
    uint codeblocks() {
      return dochas["codeblock"];
    }
    uint tables() {
      return dochas["table"];
    }
    uint blocks() {
      return dochas["block"];
    }
    uint groups() {
      return dochas["group"];
    }
    uint poems() {
      return dochas["poem"];
    }
    uint quotes() {
      return dochas["quote"];
    }
    ulong images() { // TODO not ideal rethink
      return (image_list.to!string.strip("[","]").split(",").length);
    }
    auto imagelist() {
      return image_list;
    }
    auto keys_seq() {
      return docSectKeysSeq!()(document_section_keys_sequenced);
    }
    string[] segnames_lv4() {
      return segnames_4;
    }
    string[] segnames_lv_0_to_4() {
      return segnames_0_to_4;
    }
    string[string][string] tag_associations() {
      return tag_assoc;
    }
  }
  return DocHas_();
}
#+END_SRC

*** _return the document tuple_ [#A] :return:tuple:

#+NAME: abs_return_tuple
#+BEGIN_SRC d
auto t = tuple(
  document_the,
  doc_has,
);
return t;
#+END_SRC

** 5. Functions :abstract:function:

functions used in document abstraction

*** set & resets :reset:
**** object reset: remove (clean) :object:remove:

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

**** set, initialize or re-initialize :set:

#+NAME: abs_functions_header_set_common
#+BEGIN_SRC d
@system void flow_common_reset_()(
  return ref int[string]     line_occur,
  return ref string[string]  an_object,
  return ref 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;
}
#+END_SRC

*** check object_number status in document :ocn:

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

*** make substitutions
**** project

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

**** fontface

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

*** block :block:
**** block start (open) block :start:
***** { block starts function

#+NAME: abs_functions_block_open
#+BEGIN_SRC d
@safe void flow_txt_block_start()(
             char[]         line,
  return ref uint[string]   pith,
  return ref uint[string]   dochas,
  return ref string[string] object_number_poem
) {
#+END_SRC

****** block (various) curly open :curly:

#+NAME: abs_functions_block_regex
#+BEGIN_SRC d
static auto rgx = RgxI();
#+END_SRC

******* code

#+NAME: abs_functions_block_curly_open_code
#+BEGIN_SRC d
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) {                              // code (curly) open
    writefln(
      "* [code curly] %s",
      line
    );
  }
  pith["block_is"]                = eN.blk_is.code;
  pith["block_state"]             = eN.blk_state.on;
  pith["block_delim"]             = eN.blk_delim.curly;
#+END_SRC

******* poem

#+NAME: abs_functions_block_curly_open_poem
#+BEGIN_SRC d
} 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) {                              // poem (curly) open
    writefln(
      "* [poem curly] %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.curly;
  pith["verse_new"]               = eN.bi.on;
#+END_SRC

******* group

#+NAME: abs_functions_block_curly_open_group
#+BEGIN_SRC d
} 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) {                             // group (curly) open
    writefln(
      "* [group curly] %s",
      line
    );
  }
  pith["block_is"]                = eN.blk_is.group;
  pith["block_state"]             = eN.blk_state.on;
  pith["block_delim"]             = eN.blk_delim.curly;
#+END_SRC

******* block

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

******* quote

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

******* table

#+NAME: abs_functions_block_curly_open_table
#+BEGIN_SRC d
} else if (auto m = line.matchFirst(rgx.block_curly_table_open)) {           /+ curly table open +/
  debug(table) {                             // table (curly) open
    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;
#+END_SRC

******* table special

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

****** block (various) tic open :tic:
******* code

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

******* poem

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

******* group

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

******* block

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

******* quote

#+NAME: abs_functions_block_tic_open_quote
#+BEGIN_SRC d
} 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) {                             // quote (tic) open
    writefln(
      "* [quote tic] %s",
      line
    );
  }
  pith["block_is"]                = eN.blk_is.quote;
  pith["block_state"]             = eN.blk_state.on;
  pith["block_delim"]             = eN.blk_delim.tic;
#+END_SRC

******* table

#+NAME: abs_functions_block_tic_open_table
#+BEGIN_SRC d
} else if (auto m = line.matchFirst(rgx.block_tic_table_open)) {             /+ tic table open +/
  debug(table) {                             // table (tic) open
    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;
}
#+END_SRC

***** }

#+NAME: abs_functions_block_close
#+BEGIN_SRC d
}
#+END_SRC

**** block continue (an open block) :continue:
***** _code block_ (special status, deal with first) :code:

#+NAME: abs_functions_block_code
#+BEGIN_SRC d
@safe void flow_txt_block_code()(
             char[]          line,
  return ref string[string]  an_object,
  return ref 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";
      }
    }
  }
}
#+END_SRC

***** biblio block :biblio:
****** biblio map tags

#+NAME: abs_functions_block_biblio_map_tags
#+BEGIN_SRC d
@safe final 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];
}
#+END_SRC

******* +consider+

#+NAME: none
#+BEGIN_SRC d
final string biblio_tag_map_()(string abr) {
  string name;
  switch (abr) {
  case "au"     : name = "author_raw"; break;
  case "ed"     : name = "editor_raw"; break;
  case "ti"     : name = "fulltitle";  break;
  case "lng"    : name = "language";   break;
  case "jo"     : name = "journal";    break;
  case "vol"    : name = "volume";     break;
  case "edn"    : name = "edition";    break;
  case "yr"     : name = "year";       break;
  case "pl"     : name = "place";      break;
  case "pb"     : name = "publisher";  break;
  case "pub"    : name = "publisher";  break;
  case "pg"     : name = "pages";      break;
  case "pgs"    : name = "pages";      break;
  case "sn"     : name = "short_name"; break;
  default       : name = abr;          break;
  }
  return name;
}
#+END_SRC

****** biblio block

#+NAME: abs_functions_block_biblio_text_block
#+BEGIN_SRC d
@system void flow_txt_block_biblio(
  char[]                  line,
  return ref uint[string] pith,
  return ref int          bib_entry,
  return ref string       biblio_entry_str_json,
  return ref string[]     biblio_arr_json,
) {
  mixin spineBiblio;
  auto jsn = BibJsnStr();
  static auto rgx = RgxI();
  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        = "";
  }
}
#+END_SRC

***** quote block :quote:

#+NAME: abs_functions_block_quote
#+BEGIN_SRC d
@safe string[string] flow_txt_block_quote()(
             char[]          line,
             string[string]  an_object,
  return ref 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";
      }
    }
  }
  return an_object;
}
#+END_SRC

***** group block :group:

- apply inline markup
- discard leading and newline whitespace

#+NAME: abs_functions_block_group
#+BEGIN_SRC d
@safe string[string] flow_txt_block_group()(
             char[]          line,
             string[string]  an_object,
  return ref 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";
      }
    }
  }
  return an_object;
}
#+END_SRC

***** block block :block:

- apply inline markup
- keep whitespace indentation
- keep newlines

#+NAME: abs_functions_block_block
#+BEGIN_SRC d
@safe string[string] flow_txt_block_block()(
             char[]          line,
             string[string]  an_object,
  return ref 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";
      }
    }
  }
  return an_object;
}
#+END_SRC

***** poem block, verse objects :poem:verse:

why extra object stuff only in poem/verse?

#+NAME: abs_functions_block_poem
#+BEGIN_SRC d
@safe string[string]  flow_txt_block_poem(CMM)(
             char[]          line,
             string[string]  an_object,
  return ref uint[string]    pith,
  return ref 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";
            TxtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_tuple
              = 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_tuple[sObj.content];
            anchor_tag                                        = substantive_obj_misc_tuple[sObj.anchor_tag];
            comp_obj_block                                    = comp_obj_block.init;
            comp_obj_block.metainfo.is_of_part                = "body";
            comp_obj_block.metainfo.is_of_section             = "body";
            comp_obj_block.metainfo.is_of_type                = "block";
            comp_obj_block.metainfo.is_a                      = "verse";
            comp_obj_block.metainfo.ocn                       = obj_cite_digits.object_number;
            comp_obj_block.metainfo.identifier                = obj_cite_digits.identifier;
            comp_obj_block.metainfo.object_number_off         = obj_cite_digits.off;
            comp_obj_block.metainfo.o_n_book_index            = obj_cite_digits.bkidx;
            comp_obj_block.metainfo.object_number_type        = obj_cite_digits.type;
            comp_obj_block.tags.html_segment_anchor_tag_is    = tag_in_seg["seg_lv4"];
            comp_obj_block.tags.epub_segment_anchor_tag_is    = tag_in_seg["seg_lv1to4"];
            comp_obj_block.text                               = an_object["substantive"];
            comp_obj_block.has.inline_notes_reg               = substantive_obj_misc_tuple[sObj.notes_reg];
            comp_obj_block.has.inline_notes_star              = substantive_obj_misc_tuple[sObj.notes_star];
            comp_obj_block.has.inline_links                   = substantive_obj_misc_tuple[sObj.links];
            the_document_body_section                         ~= comp_obj_block;
            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"]
          );
          TxtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_tuple
            = 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_tuple[sObj.content];
          anchor_tag                                          = substantive_obj_misc_tuple[sObj.anchor_tag];
          comp_obj_block                                      = comp_obj_block.init;
          comp_obj_block.metainfo.is_of_part                  = "body";
          comp_obj_block.metainfo.is_of_section               = "body";
          comp_obj_block.metainfo.is_of_type                  = "block";
          comp_obj_block.metainfo.is_a                        = "verse";
          comp_obj_block.metainfo.ocn                         = obj_cite_digits.object_number;
          comp_obj_block.metainfo.identifier                  = obj_cite_digits.identifier;
          comp_obj_block.metainfo.object_number_off           = obj_cite_digits.off;
          comp_obj_block.metainfo.o_n_book_index              = obj_cite_digits.bkidx;
          comp_obj_block.metainfo.object_number_type          = obj_cite_digits.type;
          comp_obj_block.tags.html_segment_anchor_tag_is      = tag_in_seg["seg_lv4"];
          comp_obj_block.tags.epub_segment_anchor_tag_is      = tag_in_seg["seg_lv1to4"];
          comp_obj_block.text                                 = an_object["substantive"];
          comp_obj_block.has.inline_notes_reg                 = substantive_obj_misc_tuple[sObj.notes_reg];
          comp_obj_block.has.inline_notes_star                = substantive_obj_misc_tuple[sObj.notes_star];
          comp_obj_block.has.inline_links                     = substantive_obj_misc_tuple[sObj.links];
          the_document_body_section                           ~= comp_obj_block;
          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";
          TxtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_tuple
            = 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_tuple[sObj.content];
          anchor_tag                                          = substantive_obj_misc_tuple[sObj.anchor_tag];
          comp_obj_block                                      = comp_obj_block.init;
          comp_obj_block.metainfo.is_of_part                  = "body";
          comp_obj_block.metainfo.is_of_section               = "body";
          comp_obj_block.metainfo.is_of_type                  = "block";
          comp_obj_block.metainfo.is_a                        = "verse";
          comp_obj_block.metainfo.ocn                         = obj_cite_digits.object_number;
          comp_obj_block.metainfo.identifier                  = obj_cite_digits.identifier;
          comp_obj_block.metainfo.object_number_off           = obj_cite_digits.off;
          comp_obj_block.metainfo.o_n_book_index              = obj_cite_digits.bkidx;
          comp_obj_block.metainfo.object_number_type          = obj_cite_digits.type;
          comp_obj_block.tags.html_segment_anchor_tag_is      = tag_in_seg["seg_lv4"];
          comp_obj_block.tags.epub_segment_anchor_tag_is      = tag_in_seg["seg_lv1to4"];
          comp_obj_block.text                                 = an_object["substantive"];
          comp_obj_block.has.inline_notes_reg                 = substantive_obj_misc_tuple[sObj.notes_reg];
          comp_obj_block.has.inline_notes_star                = substantive_obj_misc_tuple[sObj.notes_star];
          comp_obj_block.has.inline_links                     = substantive_obj_misc_tuple[sObj.links];
          the_document_body_section                           ~= comp_obj_block;
          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"]
            );
          TxtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_tuple
            = 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_tuple[sObj.content];
          anchor_tag                                          = substantive_obj_misc_tuple[sObj.anchor_tag];
          comp_obj_block                                      = comp_obj_block.init;
          comp_obj_block.metainfo.is_of_part                  = "body";
          comp_obj_block.metainfo.is_of_section               = "body";
          comp_obj_block.metainfo.is_of_type                  = "block";
          comp_obj_block.metainfo.is_a                        = "verse";
          comp_obj_block.metainfo.ocn                         = obj_cite_digits.object_number;
          comp_obj_block.metainfo.identifier                  = obj_cite_digits.identifier;
          comp_obj_block.metainfo.object_number_off           = obj_cite_digits.off;
          comp_obj_block.metainfo.o_n_book_index              = obj_cite_digits.bkidx;
          comp_obj_block.metainfo.object_number_type          = obj_cite_digits.type;
          comp_obj_block.tags.html_segment_anchor_tag_is      = tag_in_seg["seg_lv4"];
          comp_obj_block.tags.epub_segment_anchor_tag_is      = tag_in_seg["seg_lv1to4"];
          comp_obj_block.text                                 = an_object["substantive"];
          comp_obj_block.has.inline_notes_reg                 = substantive_obj_misc_tuple[sObj.notes_reg];
          comp_obj_block.has.inline_notes_star                = substantive_obj_misc_tuple[sObj.notes_star];
          comp_obj_block.has.inline_links                     = substantive_obj_misc_tuple[sObj.links];
          the_document_body_section                           ~= comp_obj_block;
          tag_assoc                                           = an_object.inline_para_link_anchor(tag_in_seg, tag_assoc);
          object_reset(an_object);
          processing.remove("verse");
          ++cntr;
        }
      }
    }
  }
  return an_object;
}
#+END_SRC

***** table block :table:

there are 3 types of table markup that need to be nomalized (given a single representation) here

- curly brace block
- tic block
- special notation block

you need:
- identify the type for the munging to create uniform presentation
  - curly, tick, special
  - table heading row, bool
- present table header info in uniform way
  - table_number_of_columns, int (count)
  - table_column_widths, int[] column widths (as given or calculate average)
  - show table walls, bool
- table content marked up in uniform way

#+NAME: abs_functions_block_table
#+BEGIN_SRC d
@system string[string] flow_txt_block_table(CMM)(
             char[]          line,
             string[string]  an_object,
  return ref uint[string]    pith,
  return ref 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;
        line.flow_table_closed_make_special_notation_table_(
          an_object,
          the_document_body_section,
          obj_cite_digits,
          comp_obj_heading,
          cntr,
          pith,
          conf_make_meta,
        );
      } 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";
      }
    }
  }
  return an_object;
}
#+END_SRC

**** special table notation, make: table

process and use an_object["table_head"] (then empty it)
- present table header info in uniform way
  - table_number_of_columns, int (count)
  - table_column_widths, int[] column widths (as given or calculate average)
  - show table walls, bool

#+NAME: abs_functions_block_line_status_empty_table_closed
#+BEGIN_SRC d
@system void flow_table_closed_make_special_notation_table_(N,CMM)(
             char[]                line,
  return ref string[string]        an_object,
  return ref ObjGenericComposite[] the_document_body_section,
  return ref N                     obj_cite_digits,
  return ref ObjGenericComposite   _comp_obj_heading,
  return ref int                   cntr,
  return ref uint[string]          pith,
  CMM                              conf_make_meta
) {
  comp_obj_block = comp_obj_block.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";
  TxtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_tuple
    = 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_tuple[sObj.content];
  comp_obj_block.metainfo.ocn                                 = obj_cite_digits.object_number;
  comp_obj_block.metainfo.identifier                          = obj_cite_digits.identifier;
  comp_obj_block.metainfo.object_number_off                   = obj_cite_digits.off;
  comp_obj_block.tags.html_segment_anchor_tag_is              = tag_in_seg["seg_lv4"];
  comp_obj_block.tags.epub_segment_anchor_tag_is              = tag_in_seg["seg_lv1to4"];
  comp_obj_block.metainfo.o_n_book_index                      = obj_cite_digits.bkidx;
  comp_obj_block.metainfo.object_number_type                  = obj_cite_digits.type;
  comp_obj_block                                              = comp_obj_block.flow_table_instructions(an_object["table_head"]);
  comp_obj_block                                              = comp_obj_block.flow_table_substantive_munge_special(an_object["substantive"]);
  the_document_body_section                                   ~= comp_obj_block;
  object_reset(an_object);
  processing.remove("verse");
  ++cntr;
}
#+END_SRC

**** block end (close an open block): line empty, block flag _makes_ :close:

***** { line empty, _make block_

#+NAME: abs_functions_block_line_status_empty_block_close_function_open
#+BEGIN_SRC d
@system string[string] flow_block_flag_line_empty_(B,N,CMM,Ts)(
             char[]                   line,
             string[string]           an_object,
             B                        bookindex_extract_hash,
  return ref ObjGenericComposite[]    the_document_body_section,
  return ref string[][string][string] bookindex_unordered_hashes,
  return ref N                        obj_cite_digits,
  return ref ObjGenericComposite      _comp_obj_heading,
  return ref int                      cntr,
  return ref 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) {
#+END_SRC

****** make: quote block

#+NAME: abs_functions_block_line_status_empty_block_quote
#+BEGIN_SRC d
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"]
    );
  TxtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_tuple
    = 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_tuple[sObj.content];
  anchor_tag                                              = substantive_obj_misc_tuple[sObj.anchor_tag];
  comp_obj_block                                          = comp_obj_block.init;
  comp_obj_block.metainfo.is_of_part                      = "body";
  comp_obj_block.metainfo.is_of_section                   = "body";
  comp_obj_block.metainfo.is_of_type                      = "block";
  comp_obj_block.metainfo.is_a                            = "quote";
  comp_obj_block.metainfo.ocn                             = obj_cite_digits.object_number;
  comp_obj_block.metainfo.identifier                      = obj_cite_digits.identifier;
  comp_obj_block.metainfo.object_number_off               = obj_cite_digits.off;
  comp_obj_block.metainfo.o_n_book_index                  = obj_cite_digits.bkidx;
  comp_obj_block.metainfo.object_number_type              = obj_cite_digit_type;
  comp_obj_block.metainfo.lang                            = an_object["lang"];
  comp_obj_block.metainfo.attrib                          = an_object["attrib"];
  comp_obj_block.tags.html_segment_anchor_tag_is          = tag_in_seg["seg_lv4"];
  comp_obj_block.tags.epub_segment_anchor_tag_is          = tag_in_seg["seg_lv1to4"];
  comp_obj_block.text                                     = an_object["substantive"];
  comp_obj_block.has.inline_notes_reg                     = substantive_obj_misc_tuple[sObj.notes_reg];
  comp_obj_block.has.inline_notes_star                    = substantive_obj_misc_tuple[sObj.notes_star];
  comp_obj_block.has.inline_links                         = substantive_obj_misc_tuple[sObj.links];
  the_document_body_section                               ~= comp_obj_block;
  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;
#+END_SRC

****** make: group block

#+NAME: abs_functions_block_line_status_empty_block_group
#+BEGIN_SRC d
} 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"]
    );
  TxtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_tuple
    = 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_tuple[sObj.content];
  anchor_tag                                              = substantive_obj_misc_tuple[sObj.anchor_tag];
  comp_obj_block                                          = comp_obj_block.init;
  comp_obj_block.metainfo.is_of_part                      = "body";
  comp_obj_block.metainfo.is_of_section                   = "body";
  comp_obj_block.metainfo.is_of_type                      = "block";
  comp_obj_block.metainfo.is_a                            = "group";
  comp_obj_block.metainfo.ocn                             = obj_cite_digits.object_number;
  comp_obj_block.metainfo.identifier                      = obj_cite_digits.identifier;
  comp_obj_block.metainfo.object_number_off               = obj_cite_digits.off;
  comp_obj_block.metainfo.o_n_book_index                  = obj_cite_digits.bkidx;
  comp_obj_block.metainfo.object_number_type              = obj_cite_digits.type;
  comp_obj_block.metainfo.lang                            = an_object["lang"];
  comp_obj_block.metainfo.attrib                          = an_object["attrib"];
  comp_obj_block.tags.html_segment_anchor_tag_is          = tag_in_seg["seg_lv4"];
  comp_obj_block.tags.epub_segment_anchor_tag_is          = tag_in_seg["seg_lv1to4"];
  comp_obj_block.text                                     = an_object["substantive"];
  comp_obj_block.has.inline_notes_reg                     = substantive_obj_misc_tuple[sObj.notes_reg];
  comp_obj_block.has.inline_notes_star                    = substantive_obj_misc_tuple[sObj.notes_star];
  comp_obj_block.has.inline_links                         = substantive_obj_misc_tuple[sObj.links];
  the_document_body_section                               ~= comp_obj_block;
  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;
#+END_SRC

****** make: block

#+NAME: abs_functions_block_line_status_empty_block_block
#+BEGIN_SRC d
} 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"]
    );
  TxtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_tuple
    = 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_tuple[sObj.content];
  // anchor_tag                                           = substantive_obj_misc_tuple[sObj.anchor_tag];
  comp_obj_block                                          = comp_obj_block.init;
  comp_obj_block.metainfo.is_of_part                      = "body";
  comp_obj_block.metainfo.is_of_section                   = "body";
  comp_obj_block.metainfo.is_of_type                      = "block";
  comp_obj_block.metainfo.is_a                            = "block";
  comp_obj_block.metainfo.ocn                             = obj_cite_digits.object_number;
  comp_obj_block.metainfo.identifier                      = obj_cite_digits.identifier;
  comp_obj_block.metainfo.object_number_off               = obj_cite_digits.off;
  comp_obj_block.metainfo.o_n_book_index                  = obj_cite_digits.bkidx;
  comp_obj_block.metainfo.object_number_type              = obj_cite_digit_type;
  comp_obj_block.metainfo.lang                            = an_object["lang"];
  comp_obj_block.metainfo.attrib                          = an_object["attrib"];
  comp_obj_block.tags.html_segment_anchor_tag_is          = tag_in_seg["seg_lv4"];
  comp_obj_block.tags.epub_segment_anchor_tag_is          = tag_in_seg["seg_lv1to4"];
  comp_obj_block.text                                     = an_object["substantive"];
  comp_obj_block.has.inline_notes_reg                     = substantive_obj_misc_tuple[sObj.notes_reg];
  comp_obj_block.has.inline_notes_star                    = substantive_obj_misc_tuple[sObj.notes_star];
  comp_obj_block.has.inline_links                         = substantive_obj_misc_tuple[sObj.links];
  the_document_body_section                               ~= comp_obj_block;
  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;
#+END_SRC

****** make: poem

#+NAME: abs_functions_block_line_status_empty_block_poem
#+BEGIN_SRC d
} 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                                       = comp_obj_poem_ocn.init;
  comp_obj_poem_ocn.metainfo.is_of_part                   = "body";
  comp_obj_poem_ocn.metainfo.is_of_section                = "body";
  comp_obj_poem_ocn.metainfo.is_of_type                   = "block";
  comp_obj_poem_ocn.metainfo.is_a                         = "poem";
  comp_obj_poem_ocn.metainfo.ocn                          = 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;
  comp_obj_poem_ocn.text                                  = "";
  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");
#+END_SRC

****** make: code block

#+NAME: abs_functions_block_line_status_empty_block_code
#+BEGIN_SRC d
} 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"]
    );
  TxtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_tuple
    = 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_tuple[sObj.content];
  anchor_tag                                              = substantive_obj_misc_tuple[sObj.anchor_tag];
  comp_obj_code                                           = comp_obj_code.init;
  comp_obj_code.metainfo.is_of_part                       = "body";
  comp_obj_code.metainfo.is_of_section                    = "body";
  comp_obj_code.metainfo.is_of_type                       = "block";
  comp_obj_code.metainfo.is_a                             = "code";
  comp_obj_code.metainfo.ocn                              = obj_cite_digits.object_number;
  comp_obj_code.metainfo.identifier                       = obj_cite_digits.identifier;
  comp_obj_code.metainfo.object_number_off                = obj_cite_digits.off;
  comp_obj_code.metainfo.o_n_book_index                   = obj_cite_digits.bkidx;
  comp_obj_code.metainfo.object_number_type               = obj_cite_digits.type;
  comp_obj_code.metainfo.syntax                           = an_object["syntax"];
  comp_obj_code.metainfo.attrib                           = an_object["attrib"];
  comp_obj_code.code_block.linenumbers                    = (an_object["attrib"].match(rgx.code_numbering)) ? true : false;
  comp_obj_code.tags.html_segment_anchor_tag_is           = tag_in_seg["seg_lv4"];
  comp_obj_code.tags.epub_segment_anchor_tag_is           = tag_in_seg["seg_lv1to4"];
  comp_obj_code.text                                      = an_object["substantive"];
  comp_obj_code.has.inline_notes_reg                      = substantive_obj_misc_tuple[sObj.notes_reg];
  comp_obj_code.has.inline_notes_star                     = substantive_obj_misc_tuple[sObj.notes_star];
  comp_obj_code.has.inline_links                          = substantive_obj_misc_tuple[sObj.links];
  the_document_body_section                               ~= comp_obj_code;
  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;
#+END_SRC

****** make: table

#+NAME: abs_functions_block_line_status_empty_block_table
#+BEGIN_SRC d
} else if (pith["block_is"]    == eN.blk_is.table) {
  comp_obj_block = comp_obj_block.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"]
    );
  TxtAndAnchorTagPlusHasFootnotesUrlsImages substantive_obj_misc_tuple
    = 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_tuple[sObj.content];
  comp_obj_block                                          = comp_obj_block.init;
  comp_obj_block.metainfo.ocn                             = obj_cite_digits.object_number;
  comp_obj_block.metainfo.identifier                      = obj_cite_digits.identifier;
  comp_obj_block.metainfo.object_number_off               = obj_cite_digits.off;
  comp_obj_block.tags.html_segment_anchor_tag_is          = tag_in_seg["seg_lv4"];
  comp_obj_block.tags.epub_segment_anchor_tag_is          = tag_in_seg["seg_lv1to4"];
  comp_obj_block.metainfo.o_n_book_index                  = obj_cite_digits.bkidx;
  comp_obj_block.metainfo.object_number_type              = obj_cite_digits.type;
  comp_obj_block                                          = comp_obj_block.flow_table_instructions(an_object["table_head"]);
  comp_obj_block                                          = comp_obj_block.flow_table_substantive_munge(an_object["substantive"]);
  the_document_body_section                               ~= comp_obj_block;
  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;
}
#+END_SRC

***** }

#+NAME: abs_functions_block_line_status_empty_block_close_function_close
#+BEGIN_SRC d
  }
  return an_object;
}
#+END_SRC

*** book index :bookindex:

#+NAME: abs_functions_book_index
#+BEGIN_SRC d
@system string[string] flow_book_index_(B)(
             char[]          line,
             string[string]  an_object,
  return ref string          book_idx_tmp,
  return ref 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;
      }
    }
  }
  return an_object;
}
#+END_SRC

*** heading or paragraph :heading:paragraph:
**** heading found :heading:

#+NAME: abs_functions_heading_found
#+BEGIN_SRC d
@safe string[string] flow_heading_found_()(
             char[]                line,
             string[string]        heading_match_str,
             string[]              _make_unmarked_headings,
  return ref Regex!(char)[string]  heading_match_rgx,
  return ref 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;
  }
  return heading_match_str;
}
#+END_SRC

**** heading make set :heading:

#+NAME: abs_functions_heading_make_set
#+BEGIN_SRC d
@safe char[] 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);
      }
    }
  }
  return line;
}
#+END_SRC

**** heading match :heading:

#+NAME: abs_functions_heading_matched
#+BEGIN_SRC d
@safe string[string] flow_heading_matched_(CMM)(
             char[]          line,
             string[string]  an_object,
  return ref int[string]     line_occur,
  return ref string          an_object_key,
  return ref int[string]     lv,
  return ref int[string]     collapsed_lev,
  return ref uint[string]    pith,
  return ref 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, 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);
    }
  }
  return an_object;
}
#+END_SRC

**** para match :para:

#+NAME: abs_functions_para_matched
#+BEGIN_SRC d
@safe string[string] flow_para_match_()(
             char[]         line,
             string[string]  an_object,
  return ref string          an_object_key,
  return ref int[string]     indent,
  return ref bool            bullet,
  return ref uint[string]    pith,
  return ref 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"];
  }
  return an_object;
}
#+END_SRC

**** text font face

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

**** tables

- number of columns
- column widths (either as given or uniform, first often different from rest)
- column aligns (as given else default left for text, check whether can default right for digits)
- table heading (auto align left)
- table walls
- TODO need to be able to align columns left or right (digits)

***** table instructions

#+NAME: abs_functions_table_instructions
#+BEGIN_SRC d
@safe ObjGenericComposite flow_table_instructions(H)(
  return ref 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;
}
#+END_SRC

***** table array munge

#+NAME: abs_functions_table_munge_array
#+BEGIN_SRC d
@safe ObjGenericComposite flow_table_array_munge(T)(
  return ref ObjGenericComposite  table_object,
  return ref T                    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 && comp_obj_block.table.heading) {
      } else if (col.match(rgx.numeric_col) && idx_r == 1) { // conditions reversed to avoid: gdc compiled program run segfault
        if ((comp_obj_block.table.column_aligns.length > idx_c)
        && (comp_obj_block.table.column_aligns[idx_c].matchFirst(rgx.table_col_align_match))) {
          comp_obj_block.table.column_aligns[idx_c]
            = comp_obj_block.table.column_aligns[idx_c];
        } else if (comp_obj_block.table.column_aligns.length > idx_c) {
          comp_obj_block.table.column_aligns[idx_c] = "r";
        } else {
          comp_obj_block.table.column_aligns ~= "r";
        }
      } else if (idx_r == 1) {
        if ((comp_obj_block.table.column_aligns.length > idx_c)
        && (comp_obj_block.table.column_aligns[idx_c].matchFirst(rgx.table_col_align_match))) {
          comp_obj_block.table.column_aligns[idx_c]
            = comp_obj_block.table.column_aligns[idx_c];
        } else if (comp_obj_block.table.column_aligns.length > idx_c) {
          comp_obj_block.table.column_aligns[idx_c] = "l";
        } else {
          comp_obj_block.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 (comp_obj_block.table.number_of_columns != col_num) {
    if (comp_obj_block.table.number_of_columns == 0) {
      comp_obj_block.table.number_of_columns = (col_num).to!int;
    } else {
      debug(table_dev) {
        writeln(comp_obj_block.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: ", comp_obj_block.table.column_aligns, "\n",
      "no. of columns: ", comp_obj_block.table.number_of_columns, "\n",
      "col widths: ", comp_obj_block.table.column_widths,
        " sum: ", comp_obj_block.table.column_widths.sum, "\n",
      _table_substantive);
  }
  comp_obj_block.text = _table_substantive;
  return table_object;
}
#+END_SRC

***** table substantive munge

#+NAME: abs_functions_table_munge_substantive
#+BEGIN_SRC d
@system ObjGenericComposite flow_table_substantive_munge(T)(
  return ref ObjGenericComposite  table_object,
  return ref T                    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;
  foreach(col; _table_rows) {
    _table_cols = col.split(rgx.table_col_delimiter);
    _table ~= _table_cols;
  }
  table_object = table_object.flow_table_array_munge(_table);
  return table_object;
}
#+END_SRC

***** table substantive munge special

#+NAME: abs_functions_table_munge_substantive_special
#+BEGIN_SRC d
@system ObjGenericComposite flow_table_substantive_munge_special(T)(
  return ref ObjGenericComposite  table_object,
  return ref T                    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;
  foreach(col; _table_rows) {
    _table_cols = col.split(rgx.table_col_delimiter_special);
    _table ~= _table_cols;
  }
  table_object = table_object.flow_table_array_munge(_table);
  return table_object;
}
#+END_SRC

*** function emitters :emitters:
**** object :object:
***** ocn :ocn:

#+NAME: meta_emitters_ocn
#+BEGIN_SRC d
@safe pure struct OCNemitter {
  int ocn_digit, ocn_object_number, ocn_on_, ocn_off_, ocn_bkidx, ocn_bkidx_;
  string object_identifier;
  bool ocn_is_off;
  @safe 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() {
  }
}
#+END_SRC

***** object inline markup munge :markup:inline:
****** { struct, inline markup munge

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

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

******* footnotes endnotes markup

#+NAME: meta_emitters_obj_inline_markup_munge_function_markup_footnotes_endnotes
#+BEGIN_SRC d
@safe 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;
  }
  TxtPlusHasFootnotes t = tuple(
    obj_txt_out,
    flg_notes_reg,
    flg_notes_star,
    flg_notes_plus,
  );
  return t;
}
#+END_SRC

******* object notes and links

#+NAME: meta_emitters_obj_inline_markup_munge_function_object_notes_and_links
#+BEGIN_SRC d
@safe private 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┃");
  }
  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);
    }
  }
  TxtPlusHasFootnotesUrlsImages t = tuple(
    obj_txt_out,
    ftn.has_notes_reg,
    ftn.has_notes_star,
    ftn.has_notes_plus,
    urls,
    images_without_dimensions,
  );
  return t;
}
auto init() {
  TxtPlusHasFootnotesUrlsImages t = object_notes_and_links_("");
  return t;
}
invariant() {
}
#+END_SRC

******* heading

- identified text by heading level marker followed by text until two new lines
- general markup subset
  - attributes (heading)
    - heading level
  - inline markup
    - font faces (bold, italics, underscore etc.)
    - footnotes/endnotes
    - links?

#+NAME: meta_emitters_obj_inline_markup_munge_function_heading
#+BEGIN_SRC d
@safe auto 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;
  TxtPlusHasFootnotesUrlsImages t = 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 t;
}
invariant() {
}
#+END_SRC

******* para

- paragraph the default form of text, text without other identification until two new lines
- general markup
  - attributes (para)
    - paragraph attributes (indent markup, bullet indent, etc.)
  - inline markup
    - font faces (bold, italics, underscore etc.)
    - footnotes/endnotes
    - links

#+NAME: meta_emitters_obj_inline_markup_munge_function_para
#+BEGIN_SRC d
@safe auto 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);
  TxtPlusHasFootnotesUrlsImages t = object_notes_and_links_(obj_txt["munge"]);
  debug(munge) {
    writeln(__LINE__);
    writeln(obj_txt_in);
    writeln(__LINE__);
    writeln(obj_txt["munge"].to!string);
  }
  return t;
}
#+END_SRC

******* quote WORKON

- quote block identified by open an close tags
- general markup subset
  - NO standard (para) attributes
  - inline markup
    - font faces (bold, italics, underscore etc.)
    - footnotes/endnotes
    - links

#+NAME: meta_emitters_obj_inline_markup_munge_function_quote
#+BEGIN_SRC d
@safe auto munge_quote()(string obj_txt_in) {
  TxtPlusHasFootnotesUrlsImages t = object_notes_and_links_(obj_txt_in.split("\n\n").join(" \\\\\n \\\\\n"));
  // TxtPlusHasFootnotesUrlsImages t = object_notes_and_links_(obj_txt_in);
  return t;
}
invariant() {
}
#+END_SRC

******* group

- group block identified by open an close tags
- general markup
  - NO standard (para) attributes
  - inline markup
    - (indent markup, bullet indent [usually para attributes]) kept as inline markup [not available as paragraph attributes, requires bespoke processing (for group text) downstream]
    - font faces (bold, italics, underscore etc.)
    - footnotes/endnotes
    - links
- drop spaces
- keep newlines?
  - drop linebreaks (single newlines),
  - keep parabreaks (double newlines) apply paragraph attributes

#+NAME: meta_emitters_obj_inline_markup_munge_function_group
#+BEGIN_SRC d
@safe auto munge_group(string obj_txt_in) {
  TxtPlusHasFootnotesUrlsImages t = object_notes_and_links_(obj_txt_in.split("\n\n").join("\n" ~ mkup.br_line_spaced ~ "\n")); // br_line br_line_inline br_line_spaced
  return t;
}
invariant() {
}
#+END_SRC

******* block

- group block identified by open an close tags
- general markup subset
  - NO standard (para) attributes
  - inline markup
    - font faces (bold, italics, underscore etc.)
    - footnotes/endnotes
    - links
- keep newlines & spaces
  - spaces
  - newlines?
    - newlines detected and kept?

#+NAME: meta_emitters_obj_inline_markup_munge_function_block
#+BEGIN_SRC d
@safe auto munge_block()(string obj_txt_in) {
  TxtPlusHasFootnotesUrlsImages t = object_notes_and_links_(obj_txt_in);
  return t;
}
invariant() {
}
#+END_SRC

******* verse (poem)

- sub part of poem block which is identified by open an close tags, separated from other verse by double newline
- keep newlines & spaces
  - newlines
  - indentation
- what part of general markup?
  - NO standard (para) attributes
  - inline markup
    - font faces (bold, italics, underscore etc.)
    - footnotes/endnotes
    - links?

#+NAME: meta_emitters_obj_inline_markup_munge_function_verse
#+BEGIN_SRC d
@safe auto munge_verse()(string obj_txt_in) {
  TxtPlusHasFootnotesUrlsImages t = object_notes_and_links_(obj_txt_in);
  return t;
}
invariant() {
}
#+END_SRC

******* code

- NO general markup
  - NO standard (para) attributes
  - special attributes?
    - possibly identify language object attribute for syntax for coloring
    - numbered code blocks (markup/obj attribute?)
- keep newlines & spaces
  - newlines
  - indentation
- one special character represented by mkup.nbsp ░

#+NAME: meta_emitters_obj_inline_markup_munge_function_code
#+BEGIN_SRC d
@safe string munge_code()(string obj_txt_in) {
  obj_txt_in = obj_txt_in.replaceAll(rgx.space, mkup.nbsp);
  obj_txt["munge"] = obj_txt_in;
  return obj_txt["munge"];
}
invariant() {
}
#+END_SRC

******* table

- table block identified by open an close tags
- table markup

#+NAME: meta_emitters_obj_inline_markup_munge_function_table
#+BEGIN_SRC d
@safe string munge_table()(string obj_txt_in) {
  obj_txt["munge"] = obj_txt_in;
  return obj_txt["munge"];
}
invariant() {
}
#+END_SRC

******* comment

#+NAME: meta_emitters_obj_inline_markup_munge_function_comment
#+BEGIN_SRC d
@safe string munge_comment()(string obj_txt_in) {
  obj_txt["munge"] = obj_txt_in;
  return obj_txt["munge"];
}
invariant() {
}
#+END_SRC

****** }

#+NAME: meta_emitters_obj_inline_markup_munge_function_close
#+BEGIN_SRC d
}
#+END_SRC

***** toc, tags, object inline markup :markup:inline:
****** {

#+NAME: meta_emitters_obj_inline_markup_function_open
#+BEGIN_SRC d
static struct ObjInlineMarkup {
  static auto rgx = RgxI();
  static auto munge = ObjInlineMarkupMunge();
  string[string] obj_txt;
  string anchor_tag = "";
#+END_SRC

******* object inline markup and anchor tags :markup:inline:

#+NAME: meta_emitters_obj_inline_markup_and_anchor_tags_and_misc
#+BEGIN_SRC d
@safe 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;
  bool[string] obj_notes_and_links;
  obj_notes_and_links["notes_reg"]                = false;
  obj_notes_and_links["notes_star"]               = false;
  obj_notes_and_links["links"]                    = false;
  obj_notes_and_links["image_no_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":
    obj_txt["munge"]                              = munge.munge_code(obj_txt["munge"]);
    break;
  case "table":
    obj_txt["munge"]                              = munge.munge_table(obj_txt["munge"]);
    break;
  case "comment":
    obj_txt["munge"]                              = munge.munge_comment(obj_txt["munge"]);
    break;
  case "doc_end_reset":
    munge.initialize_note_numbers();
    break;
  default:
    /+ para, heading, group, block, verse +/
    obj_txt["munge"]                              = x[0];
    obj_notes_and_links["notes_reg"]              = x[1];
    obj_notes_and_links["notes_star"]             = x[2];
    obj_notes_and_links["notes_plus"]             = x[3];
    obj_notes_and_links["links"]                  = x[4];
    obj_notes_and_links["image_no_dimensions"]    = x[5];
    break;
  }
  TxtAndAnchorTagPlusHasFootnotesUrlsImages t = tuple(
    obj_txt["munge"],
    anchor_tag,
    obj_notes_and_links["notes_reg"],
    obj_notes_and_links["notes_star"],
    obj_notes_and_links["links"],
    obj_notes_and_links["image_no_dimensions"],
  );
  anchor_tag = "";
  return t;
}
invariant() {
}
#+END_SRC

******* toc (table of contents), build, gather headings :markup:inline:

#+NAME: meta_emitters_obj_inline_markup_table_of_contents
#+BEGIN_SRC d
@safe 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_;
};
@safe ObjGenericComposite[] flow_table_of_contents_gather_headings(CMM)(
             string[string]         obj_,
             CMM                    conf_make_meta,
             string[string]         tag_in_seg,
             string                 _anchor_tag,
  return ref string[][string]       lev4_subtoc,
             ObjGenericComposite[]  the_document_toc_section,
) {
  ObjGenericComposite comp_obj_toc;
  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_toc                                  = comp_obj_toc.init;
    comp_obj_toc.metainfo.is_of_part              = "frontmatter";
    comp_obj_toc.metainfo.is_of_section           = "toc";
    comp_obj_toc.metainfo.is_of_type              = "para";
    comp_obj_toc.metainfo.is_a                    = "toc";
    comp_obj_toc.metainfo.ocn                     = 0;
    comp_obj_toc.metainfo.identifier              = "";
    comp_obj_toc.metainfo.object_number_off       = true;
    comp_obj_toc.metainfo.object_number_type      = 0;
    comp_obj_toc.metainfo.dummy_heading           = (an_object["dummy_heading_status"] == "t") ? true: false;
    comp_obj_toc.attrib.indent_hang               = indent["hang_position"];
    comp_obj_toc.attrib.indent_base               = indent["base_position"];
    comp_obj_toc.attrib.bullet                    = false;
    comp_obj_toc.text                             = toc_txt_.to!string.strip;
    comp_obj_toc.has.inline_links                 = true;
    the_document_toc_section                      ~= comp_obj_toc;
  }
  comp_obj_toc                                    = comp_obj_toc.init;
  comp_obj_toc.metainfo.is_of_part                = "frontmatter";
  comp_obj_toc.metainfo.is_of_section             = "toc";
  comp_obj_toc.metainfo.is_of_type                = "para";
  comp_obj_toc.metainfo.is_a                      = "toc";
  comp_obj_toc.metainfo.ocn                       = 0;
  comp_obj_toc.metainfo.identifier                = "";
  comp_obj_toc.metainfo.object_number_off         = true;
  comp_obj_toc.metainfo.object_number_type        = 0;
  comp_obj_toc.metainfo.dummy_heading             = (an_object["dummy_heading_status"] == "t") ? true: false;
  comp_obj_toc.attrib.bullet                      = false;
  comp_obj_toc.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;
  }
  return the_document_toc_section;
}
invariant() {
}
#+END_SRC

******* private:

#+NAME: meta_emitters_obj_inline_markup_private_function_open
#+BEGIN_SRC d
private:
#+END_SRC

******** make heading number & segment anchor tags if instructed :markup:inline:segment:anchor:tags:

#+NAME: meta_emitters_obj_inline_markup_heading_numbering_and_segment_anchor_tags
#+BEGIN_SRC d
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];
@safe 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_;
}
#+END_SRC

******** make segment anchor tags if not provided :markup:inline:segment:anchor:tags:

#+NAME: meta_emitters_obj_inline_segment_anchor_tags_manufacture
#+BEGIN_SRC d
static int heading_num_lev1 = 0;
@safe 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_;
}
#+END_SRC

****** }

#+NAME: meta_emitters_obj_inline_markup_private_function_close
#+BEGIN_SRC d
}
#+END_SRC

***** object attrib :attributes:
****** { attributes structure open, public

#+NAME: meta_emitters_obj_attributes
#+BEGIN_SRC d
struct ObjAttributes {
  string[string] _obj_attrib;
#+END_SRC

******* emitter obj attributes, public

#+NAME: meta_emitters_obj_attributes_public
#+BEGIN_SRC d
@safe string obj_attributes()(
  string              obj_is_,
  string              obj_raw,
  ObjGenericComposite _comp_obj_heading,
) {
  scope(exit) {
    destroy(obj_is_);
    destroy(obj_raw);
    destroy(_comp_obj_heading);
  }
  _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_heading);
  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() {
}
#+END_SRC

******* private

#+NAME: meta_emitters_obj_attributes_private
#+BEGIN_SRC d
private:
string _obj_attributes;
#+END_SRC

******** para & blocks

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

******** heading

#+NAME: meta_emitters_obj_attributes_private_an_attribute_txt_heading
#+BEGIN_SRC d
@safe string txt_heading()(string obj_txt_in) {
  _obj_attributes = " \"use\": \"content\","
  ~ " \"of\": \"para\","
  ~ " \"is\": \"heading\"";
  return _obj_attributes;
}
invariant() {
}
#+END_SRC

******** para

#+NAME: meta_emitters_obj_attributes_private_an_attribute_txt_para
#+BEGIN_SRC d
@safe string txt_para()(string obj_txt_in) {
  _obj_attributes = " \"use\": \"content\","
  ~ " \"of\": \"para\","
  ~ " \"is\": \"para\"";
  return _obj_attributes;
}
invariant() {
}
#+END_SRC

******** quote

#+NAME: meta_emitters_obj_attributes_private_an_attribute_txt_quote
#+BEGIN_SRC d
@safe string txt_quote()(string obj_txt_in) {
  _obj_attributes = " \"use\": \"content\","
  ~ " \"of\": \"block\","
  ~ " \"is\": \"quote\"";
  return _obj_attributes;
}
invariant() {
}
#+END_SRC

******** group

#+NAME: meta_emitters_obj_attributes_private_an_attribute_txt_group
#+BEGIN_SRC d
@safe string txt_group()(string obj_txt_in) {
  _obj_attributes = " \"use\": \"content\","
  ~ " \"of\": \"block\","
  ~ " \"is\": \"group\"";
  return _obj_attributes;
}
invariant() {
}
#+END_SRC

******** block

#+NAME: meta_emitters_obj_attributes_private_an_attribute_txt_block
#+BEGIN_SRC d
@safe string txt_block()(string obj_txt_in) {
  _obj_attributes = " \"use\": \"content\","
  ~ " \"of\": \"block\","
  ~ " \"is\": \"block\"";
  return _obj_attributes;
}
invariant() {
}
#+END_SRC

******** verse

#+NAME: meta_emitters_obj_attributes_private_an_attribute_txt_verse
#+BEGIN_SRC d
@safe string txt_verse()(string obj_txt_in) {
  _obj_attributes = " \"use\": \"content\","
  ~ " \"of\": \"block\","
  ~ " \"is\": \"verse\"";
  return _obj_attributes;
}
invariant() {
}
#+END_SRC

******** code

#+NAME: meta_emitters_obj_attributes_private_an_attribute_txt_code
#+BEGIN_SRC d
@safe string txt_code()(string obj_txt_in) {
  _obj_attributes = " \"use\": \"content\","
  ~ " \"of\": \"block\","
  ~ " \"is\": \"code\"";
  return _obj_attributes;
}
invariant() {
}
#+END_SRC

******** table

#+NAME: meta_emitters_obj_attributes_private_an_attribute_txt_table
#+BEGIN_SRC d
@safe string txt_table()(string obj_txt_in) {
  _obj_attributes = " \"use\": \"content\","
  ~ " \"of\": \"block\","
  ~ " \"is\": \"table\"";
  return _obj_attributes;
}
invariant() {
}
#+END_SRC

******** comment

#+NAME: meta_emitters_obj_attributes_private_an_attribute_txt_comment
#+BEGIN_SRC d
@safe string txt_comment()(string obj_txt_in) {
  _obj_attributes = " \"use\": \"comment\","
  ~ " \"of\": \"comment\","
  ~ " \"is\": \"comment\"";
  return _obj_attributes;
}
invariant() {
}
#+END_SRC

******** set additional attribute values, parse as json

#+NAME: meta_emitters_obj_attributes_private_json
#+BEGIN_SRC d
@safe string _set_additional_values_parse_as_json()(
  string              _obj_attrib,
  string              obj_is_,
  ObjGenericComposite _comp_obj_heading,
) {
  JSONValue oa_j = parseJSON(_obj_attrib);
  assert(
    (oa_j.type == JSON_TYPE.OBJECT)
  );
  if (obj_is_ == "heading") {
    oa_j.object["object_number"]              = _comp_obj_heading.metainfo.ocn;
    oa_j.object["lev_markup_number"]          = _comp_obj_heading.metainfo.heading_lev_markup;
    oa_j.object["lev_collapsed_number"]       = _comp_obj_heading.metainfo.heading_lev_collapsed;
    oa_j.object["heading_ptr"]                = _comp_obj_heading.ptr.heading;
    oa_j.object["doc_object_ptr"]             = _comp_obj_heading.ptr.doc_object;
  }
  oa_j.object["parent_object_number"]         = _comp_obj_heading.metainfo.parent_ocn;
  oa_j.object["parent_lev_markup_number"]     = _comp_obj_heading.metainfo.parent_lev_markup;
  _obj_attrib                                 = oa_j.toString();
  return _obj_attrib;
}
#+END_SRC

****** }

#+NAME: meta_emitters_obj_attributes_private_close
#+BEGIN_SRC d
}
#+END_SRC

**** book index :book:index:
***** book index nugget hash :hash:nugget:

#+NAME: meta_emitters_bookindex_nugget
#+BEGIN_SRC d
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;
  @safe string[][string][string] bookindex_nugget_hash(N,S)(
    string bookindex_section,
    N      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() {
  }
}
#+END_SRC

***** book index (sort &) report indented :report:indented:

#+NAME: meta_emitters_bookindex_report_indented
#+BEGIN_SRC d
struct BookIndexReportIndent {
  int mkn, skn;
  @safe 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;
    }
  }
}
#+END_SRC

***** book index (sort &) report section :report:section:
****** { book index struct open

#+NAME: meta_emitters_bookindex_report_section_function_open
#+BEGIN_SRC d
struct BookIndexReportSection {
  int  mkn, skn;
  static auto rgx = RgxI();
  static auto munge = ObjInlineMarkupMunge();
#+END_SRC

******* bookindex write section

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

******* book index (sort &) build section :report:section:

#+NAME: meta_emitters_bookindex_report_section_function_build_abstraction
#+BEGIN_SRC d
@system auto bookindex_build_abstraction_section(N,B)(
  string[][string][string] bookindex_unordered_hashes,
  N                        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_heading_, comp_obj_para;
  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_heading_                                     = comp_obj_heading_.init;
      comp_obj_heading_.metainfo.is_of_part                 = "backmatter";
      comp_obj_heading_.metainfo.is_of_section              = "bookindex";
      comp_obj_heading_.metainfo.is_of_type                 = "para";
      comp_obj_heading_.metainfo.is_a                       = "heading";
      comp_obj_heading_.text                                = "Book Index";
      comp_obj_heading_.metainfo.ocn                        = 0;
      comp_obj_heading_.metainfo.identifier                 = "";
      comp_obj_heading_.metainfo.dummy_heading              = false;
      comp_obj_heading_.metainfo.object_number_off          = false;
      comp_obj_heading_.metainfo.object_number_type         = 0;
      comp_obj_heading_.tags.segment_anchor_tag_epub        = "_part_book_index";
      comp_obj_heading_.tags.anchor_tag_html                = comp_obj_heading_.tags.segment_anchor_tag_epub;
      comp_obj_heading_.tags.in_segment_html                = "bookindex";
      comp_obj_heading_.tags.anchor_tags                    = ["section_bookindex"];
      comp_obj_heading_.metainfo.heading_lev_markup         = 1;
      comp_obj_heading_.metainfo.heading_lev_collapsed      = 1;
      comp_obj_heading_.metainfo.parent_ocn                 = 1;
      comp_obj_heading_.metainfo.parent_lev_markup          = 0;
      comp_obj_heading.has.inline_links                     = true;
      bookindex_section                                     ~= comp_obj_heading_;
      tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
      tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.tags.segment_anchor_tag_epub;
      ++mkn;
    }
    {
      comp_obj_heading_                                     = comp_obj_heading_.init;
      comp_obj_heading_.metainfo.is_of_part                 = "backmatter";
      comp_obj_heading_.metainfo.is_of_section              = "bookindex";
      comp_obj_heading_.metainfo.is_of_type                 = "para";
      comp_obj_heading_.metainfo.is_a                       = "heading";
      comp_obj_heading_.text                                = "Index";
      comp_obj_heading_.metainfo.ocn                        = 0;
      comp_obj_heading_.metainfo.identifier                 = "";
      comp_obj_heading_.metainfo.dummy_heading              = true;
      comp_obj_heading_.metainfo.object_number_off          = true;
      comp_obj_heading_.metainfo.object_number_type         = 0;
      comp_obj_heading_.tags.segment_anchor_tag_epub        = "bookindex";
      comp_obj_heading_.tags.anchor_tag_html                = comp_obj_heading_.tags.segment_anchor_tag_epub;
      comp_obj_heading_.tags.in_segment_html                = comp_obj_heading_.tags.anchor_tag_html;
      comp_obj_heading_.metainfo.heading_lev_markup         = 4;
      comp_obj_heading_.metainfo.heading_lev_collapsed      = 2;
      comp_obj_heading_.metainfo.parent_ocn                 = 1;
      comp_obj_heading_.metainfo.parent_lev_markup          = 0;
      comp_obj_heading.has.inline_links                     = false;
      comp_obj_heading_.tags.anchor_tags                    = ["bookindex"];
      bookindex_section                                     ~= comp_obj_heading_;
      tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
      tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.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_para                                          = comp_obj_para.init;
      comp_obj_para.metainfo.is_of_part                      = "backmatter";
      comp_obj_para.metainfo.is_of_section                   = "bookindex";
      comp_obj_para.metainfo.is_of_type                      = "para";
      comp_obj_para.metainfo.is_a                            = "bookindex";
      comp_obj_para.text                                     = bi_tmp.to!string.strip;
      comp_obj_para.metainfo.ocn                             = 0;
      comp_obj_para.metainfo.identifier                      = "";
      comp_obj_para.metainfo.object_number_off               = true;
      comp_obj_para.metainfo.object_number_type              = 0;
      comp_obj_para.tags.anchor_tags                         = bi_tmp_tags;
      comp_obj_para.attrib.indent_hang                       = 0;
      comp_obj_para.attrib.indent_base                       = 1;
      comp_obj_para.attrib.bullet                            = false;
      comp_obj_para.has.inline_links                         = true;
      comp_obj_para.text                                     = bi_tmp.to!string.strip;
      bookindex_section                                      ~= comp_obj_para;
      ++mkn;
    }
  } else {                              // no book index, (figure out what to do here)
    comp_obj_heading_                                       = comp_obj_heading_.init;
    comp_obj_heading_.text                                  = "(skip) there is no Book Index";
    comp_obj_heading_.metainfo.ocn                          = 0;
    comp_obj_heading_.metainfo.identifier                   = "";
    comp_obj_heading_.metainfo.dummy_heading                = true;
    comp_obj_heading_.metainfo.object_number_off            = true;
    comp_obj_heading_.metainfo.object_number_type           = 0;
    comp_obj_heading_.metainfo.heading_lev_markup           = 1;
    comp_obj_heading_.metainfo.heading_lev_collapsed        = 1;
    comp_obj_heading_.metainfo.parent_ocn                   = 1;
    comp_obj_heading_.metainfo.parent_lev_markup            = 0;
    bookindex_section                                       ~= comp_obj_heading_;
  }
  auto t = tuple(
    bookindex_section,
    obj_cite_digits
  );
  return t;
}
#+END_SRC

****** }

#+NAME: meta_emitters_bookindex_report_section_function_close
#+BEGIN_SRC d
}
#+END_SRC

**** (end)notes section :endnotes:section:

#+NAME: meta_emitters_endnotes_function_open
#+BEGIN_SRC d
struct NotesSection {
  string[string] object_notes;
  int previous_count;
  int mkn;
  static auto rgx = RgxI();
#+END_SRC

***** { gather notes for endnote section struct open

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

****** gathered notes

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

****** endnote objects

#+NAME: meta_emitters_endnotes_function_endnote_objects
#+BEGIN_SRC d
@safe private auto endnote_objects(N,O)(
  N              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_heading_;
  if ((endnotes_["notes"].length > 0)
  && (opt_action.backmatter && opt_action.section_endnotes)) {
    {
      comp_obj_heading_                                     = comp_obj_heading_.init;
      comp_obj_heading_.metainfo.is_of_part                 = "backmatter";
      comp_obj_heading_.metainfo.is_of_section              = "endnotes";
      comp_obj_heading_.metainfo.is_of_type                 = "para";
      comp_obj_heading_.metainfo.is_a                       = "heading";
      comp_obj_heading_.text                                = "Endnotes";
      comp_obj_heading_.metainfo.ocn                        = 0;
      comp_obj_heading_.metainfo.identifier                 = "";
      comp_obj_heading_.metainfo.dummy_heading              = false;
      comp_obj_heading_.metainfo.object_number_off          = false;
      comp_obj_heading_.metainfo.object_number_type         = 0;
      comp_obj_heading_.tags.segment_anchor_tag_epub        = "_part_endnotes";
      comp_obj_heading_.tags.anchor_tag_html                = comp_obj_heading_.tags.segment_anchor_tag_epub;
      comp_obj_heading_.tags.in_segment_html                = "endnotes";
      comp_obj_heading_.tags.anchor_tags                    = ["section_endnotes"];
      comp_obj_heading_.metainfo.heading_lev_markup         = 1;
      comp_obj_heading_.metainfo.heading_lev_collapsed      = 1;
      comp_obj_heading_.metainfo.parent_ocn                 = 1;
      comp_obj_heading_.metainfo.parent_lev_markup          = 0;
      the_document_endnotes_section                         ~= comp_obj_heading_;
      tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
      tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.tags.segment_anchor_tag_epub;
      ++mkn;
    }
    {
      comp_obj_heading_                                     = comp_obj_heading_.init;
      comp_obj_heading_.metainfo.is_of_part                 = "backmatter";
      comp_obj_heading_.metainfo.is_of_section              = "endnotes";
      comp_obj_heading_.metainfo.is_of_type                 = "para";
      comp_obj_heading_.metainfo.is_a                       = "heading";
      comp_obj_heading_.text                                = "Endnotes";
      comp_obj_heading_.metainfo.ocn                        = 0;
      comp_obj_heading_.metainfo.identifier                 = "";
      comp_obj_heading_.metainfo.dummy_heading              = true;
      comp_obj_heading_.metainfo.object_number_off          = true;
      comp_obj_heading_.metainfo.object_number_type         = 0;
      comp_obj_heading_.tags.segment_anchor_tag_epub        = "endnotes";
      comp_obj_heading_.tags.anchor_tag_html                = comp_obj_heading_.tags.segment_anchor_tag_epub;
      comp_obj_heading_.tags.in_segment_html                = comp_obj_heading_.tags.anchor_tag_html;
      comp_obj_heading_.metainfo.heading_lev_markup         = 4;
      comp_obj_heading_.metainfo.heading_lev_collapsed      = 2;
      comp_obj_heading_.metainfo.parent_ocn                 = 1;
      comp_obj_heading_.metainfo.parent_lev_markup          = 0;
      comp_obj_heading_.tags.anchor_tags                    = ["endnotes"];
      the_document_endnotes_section                         ~= comp_obj_heading_;
      tag_assoc[comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = comp_obj_heading_.tags.in_segment_html;
      tag_assoc[comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = comp_obj_heading_.tags.segment_anchor_tag_epub;
      ++mkn;
    }
  } else {
    comp_obj_heading_                                       = comp_obj_heading_.init;
    comp_obj_heading_.metainfo.is_of_part                   = "empty";
    comp_obj_heading_.metainfo.is_of_section                = "empty";
    comp_obj_heading_.metainfo.is_of_type                   = "para";
    comp_obj_heading_.metainfo.is_a                         = "heading";
    comp_obj_heading_.text                                  = "(skip) there are no Endnotes";
    comp_obj_heading_.metainfo.ocn                          = 0;
    comp_obj_heading_.metainfo.identifier                   = "";
    comp_obj_heading_.metainfo.dummy_heading                = true;
    comp_obj_heading_.metainfo.object_number_off            = true;
    comp_obj_heading_.metainfo.object_number_type           = 0;
    comp_obj_heading_.metainfo.heading_lev_markup           = 1;
    comp_obj_heading_.metainfo.heading_lev_collapsed        = 1;
    comp_obj_heading_.metainfo.parent_ocn                   = 1;
    comp_obj_heading_.metainfo.parent_lev_markup            = 0;
    the_document_endnotes_section                           ~= comp_obj_heading_;
  }
  if (opt_action.backmatter && opt_action.section_endnotes) {
    ObjGenericComposite comp_obj_endnote_;
    comp_obj_endnote_                                       = comp_obj_endnote_.init;
    comp_obj_endnote_.metainfo.is_of_part                   = "backmatter";
    comp_obj_endnote_.metainfo.is_of_section                = "endnotes";
    comp_obj_endnote_.metainfo.is_of_type                   = "para";
    comp_obj_endnote_.metainfo.is_a                         = "endnote";
    comp_obj_endnote_.metainfo.ocn                          = 0;
    comp_obj_endnote_.metainfo.identifier                   = "";
    // comp_obj_heading_.metainfo.dummy_heading                = false;
    comp_obj_heading_.metainfo.object_number_off            = true;
    comp_obj_heading_.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_;
    }
  }
  auto t = tuple(the_document_endnotes_section, obj_cite_digits);
  return t;
}
#+END_SRC

***** }

#+NAME: meta_emitters_endnotes_function_close
#+BEGIN_SRC d
}
#+END_SRC

**** bibliography :bibliography:
***** { biblio struct

#+NAME: meta_emitters_bibliography_function_open
#+BEGIN_SRC d
struct Bibliography {
#+END_SRC

****** biblio

#+NAME: meta_emitters_bibliography_function_biblio_sorted
#+BEGIN_SRC d
@system public JSONValue[] flow_bibliography_()(
  return ref string[]    biblio_unsorted_incomplete,
  return ref 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++;
    }
  }
  return biblio_sorted__;
}
#+END_SRC

****** biblio unsorted complete

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

****** biblio sort

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

****** biblio debug

#+NAME: meta_emitters_bibliography_function_biblio_sorted_json
#+BEGIN_SRC d
@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"]);
      }
    }
  }
}
#+END_SRC

***** }

#+NAME: meta_emitters_bibliography_function_close
#+BEGIN_SRC d
}
#+END_SRC

**** node structure metadata :structure:metadata:node:
***** { metadata node struct

#+NAME: meta_emitters_metadata_function_open
#+BEGIN_SRC d
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();
#+END_SRC

****** node metadata emitter

#+NAME: meta_emitters_metadata_function_node_location
#+BEGIN_SRC d
@safe ObjGenericComposite node_location_emitter(La,Ta,N)(
  string         lev_markup_number,
  string[string] tag_in_seg,
  La             lev_anchor_tag,
  Ta             tag_assoc,
  N              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() {
}
#+END_SRC

****** node metadata emitter heading, (including most segnames & their pointers)

#+NAME: meta_emitters_metadata_function_heading
#+BEGIN_SRC d
@safe ObjGenericComposite node_emitter_heading(Hd,TaL,TA,N,fNr,fNs,fL)(
  string         _text,
  string         lev_markup_number,
  string         lev_collapsed_number,
  Hd             dummy_heading_status,
  string[string] tag_in_seg,
  TaL            lev_anchor_tag,
  TA             tag_assoc,
  N              obj_cite_digits,
  int            cntr_,
  int            ptr_,
  string[]       lv_ancestors_txt,
  string         is_,
  int            html_segnames_ptr,
  fNr            flag_notes_reg,
  fNs            flag_notes_star,
  fL             flag_links,
) {
  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_heading_;
  _comp_obj_heading_                                        = _comp_obj_heading_.init;
  _comp_obj_heading_.metainfo.is_of_part                    = "body";
  _comp_obj_heading_.metainfo.is_of_section                 = "body";
  _comp_obj_heading_.metainfo.is_of_type                    = "para";
  _comp_obj_heading_.metainfo.is_a                          = "heading";
  _comp_obj_heading_.text                                   = _text.to!string.strip;
  _comp_obj_heading_.metainfo.ocn                           = obj_cite_digits.object_number;
  _comp_obj_heading_.metainfo.identifier                    = obj_cite_digits.identifier;
  _comp_obj_heading_.metainfo.dummy_heading                 = (dummy_heading_status == "t") ? true: false;
  _comp_obj_heading_.metainfo.object_number_off             = obj_cite_digits.off;
  // _comp_obj_heading_.metainfo.o_n_book_index             = obj_cite_digits.bkidx;
  _comp_obj_heading_.metainfo.object_number_type            = obj_cite_digits.type;
  _comp_obj_heading_.tags.segment_anchor_tag_epub           = tag_in_seg["seg_lv1to4"];
  _comp_obj_heading_.tags.anchor_tag_html                   = tag_in_seg["seg_lv4"];
  _comp_obj_heading_.tags.in_segment_html                   = _comp_obj_heading_.tags.anchor_tag_html;
  _comp_obj_heading_.tags.heading_lev_anchor_tag            = lev_anchor_tag;
  _comp_obj_heading_.tags.html_segment_anchor_tag_is        = tag_in_seg["seg_lv4"];
  _comp_obj_heading_.tags.epub_segment_anchor_tag_is        = tag_in_seg["seg_lv1to4"];
  _comp_obj_heading_.metainfo.heading_lev_markup            = (!(lev_markup_number.empty) ? lev_markup_number.to!int : 0);
  _comp_obj_heading_.metainfo.heading_lev_collapsed         = (!(lev_collapsed_number.empty) ? lev_collapsed_number.to!int : 0);
  _comp_obj_heading_.metainfo.parent_ocn                    = p_["object_number"];
  _comp_obj_heading_.metainfo.parent_lev_markup             = p_["lev_markup_number"];
  _comp_obj_heading_.tags.heading_ancestors_text            = lv_ancestors_txt;
  _comp_obj_heading_.ptr.doc_object                         = cntr_;
  _comp_obj_heading_.ptr.html_segnames                      = ((lev_markup_number == "4") ? html_segnames_ptr : 0);
  _comp_obj_heading_.ptr.heading                            = ptr_;
  _comp_obj_heading_.has.inline_notes_reg                   = flag_notes_reg;
  _comp_obj_heading_.has.inline_notes_star                  = flag_notes_star;
  _comp_obj_heading_.has.inline_links                       = flag_links;
  tag_assoc[_comp_obj_heading_.tags.anchor_tag_html]["seg_lv4"]            = _comp_obj_heading_.tags.in_segment_html;
  tag_assoc[_comp_obj_heading_.tags.segment_anchor_tag_epub]["seg_lv1to4"] = _comp_obj_heading_.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_heading_.metainfo.parent_lev_markup <= 7);
  assert(_comp_obj_heading_.metainfo.parent_ocn >= 0);
  if (lev_markup_number.match(rgx.levels_numbered_headings)) {
    assert(_comp_obj_heading_.metainfo.heading_lev_markup <= 7);
    assert(_comp_obj_heading_.metainfo.ocn >= 0);
    if (_comp_obj_heading_.metainfo.parent_lev_markup > 0) {
      assert(_comp_obj_heading_.metainfo.parent_lev_markup < _comp_obj_heading_.metainfo.heading_lev_markup);
      if (_comp_obj_heading_.metainfo.ocn != 0) {
        assert(_comp_obj_heading_.metainfo.parent_ocn < _comp_obj_heading_.metainfo.ocn);
      }
    }
    if (_comp_obj_heading_.metainfo.heading_lev_markup == 0) {
      assert(_comp_obj_heading_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_sect_A);
    } else if  (_comp_obj_heading_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_sect_B) {
      assert(_comp_obj_heading_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_sect_A);
    } else if  (_comp_obj_heading_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_sect_C) {
      assert(_comp_obj_heading_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_sect_B);
    } else if  (_comp_obj_heading_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_sect_D) {
      assert(_comp_obj_heading_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_sect_C);
    } else if  (_comp_obj_heading_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_text_1) {
      assert(_comp_obj_heading_.metainfo.parent_lev_markup <= DocStructMarkupHeading.h_sect_D);
    } else if  (_comp_obj_heading_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_text_2) {
      assert(_comp_obj_heading_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_text_1);
    } else if  (_comp_obj_heading_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_text_3) {
      assert(_comp_obj_heading_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_text_2);
    } else if  (_comp_obj_heading_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_text_4) {
      assert(_comp_obj_heading_.metainfo.parent_lev_markup == DocStructMarkupHeading.h_text_3);
    } else if  (_comp_obj_heading_.metainfo.heading_lev_markup == DocStructMarkupHeading.h_text_5) {
    }
  }
  return _comp_obj_heading_;
}
invariant() {
}
#+END_SRC

***** }

#+NAME: meta_emitters_metadata_function_close
#+BEGIN_SRC d
}
#+END_SRC

*** function assertions :assertions:
**** assertions on markup document structure :doc_structure:

#+NAME: abs_functions_assertions
#+BEGIN_SRC d
@safe pure void assertions_doc_structure()(
  string[string]  an_object,
  int[string]     lv
) {
  if (lv["h3"] > eN.bi.off) {
    assert(lv["h0"] > eN.bi.off);
    assert(lv["h1"] > eN.bi.off);
    assert(lv["h2"] > eN.bi.off);
  } else if (lv["h2"] > eN.bi.off) {
    assert(lv["h0"] > eN.bi.off);
    assert(lv["h1"] > eN.bi.off);
    assert(lv["h3"] == eN.bi.off);
  } else if (lv["h1"] > eN.bi.off) {
    assert(lv["h0"] > eN.bi.off);
    assert(lv["h2"] == eN.bi.off);
    assert(lv["h3"] == eN.bi.off);
  } else if (lv["h0"] > eN.bi.off) {
    assert(lv["h1"] == eN.bi.off);
    assert(lv["h2"] == eN.bi.off);
    assert(lv["h3"] == eN.bi.off);
  } else {
    assert(lv["h0"] == eN.bi.off);
    assert(lv["h1"] == eN.bi.off);
    assert(lv["h2"] == eN.bi.off);
    assert(lv["h3"] == eN.bi.off);
  }
  if (lv["h7"] > eN.bi.off) {
    assert(lv["h4"] > eN.bi.off);
    assert(lv["h5"] > eN.bi.off);
    assert(lv["h6"] > eN.bi.off);
  } else if (lv["h6"] > eN.bi.off) {
    assert(lv["h4"] > eN.bi.off);
    assert(lv["h5"] > eN.bi.off);
    assert(lv["h7"] == eN.bi.off);
  } else if (lv["h5"] > eN.bi.off) {
    assert(lv["h4"] > eN.bi.off);
    assert(lv["h6"] == eN.bi.off);
    assert(lv["h7"] == eN.bi.off);
  } else if (lv["h4"] > eN.bi.off) {
    assert(lv["h5"] == eN.bi.off);
    assert(lv["h6"] == eN.bi.off);
    assert(lv["h7"] == eN.bi.off);
  } else {
    assert(lv["h4"] == eN.bi.off);
    assert(lv["h5"] == eN.bi.off);
    assert(lv["h6"] == eN.bi.off);
    assert(lv["h7"] == eN.bi.off);
  }
  if (lv["h0"] == eN.bi.off) {
    assert(lv["h1"] == eN.bi.off);
    assert(lv["h2"] == eN.bi.off);
    assert(lv["h3"] == eN.bi.off);
    assert(lv["h4"] == eN.bi.off);
    assert(lv["h5"] == eN.bi.off);
    assert(lv["h6"] == eN.bi.off);
    assert(lv["h7"] == eN.bi.off);
  }
  if (lv["h1"] == eN.bi.off) {
    assert(lv["h2"] == eN.bi.off);
    assert(lv["h3"] == eN.bi.off);
  }
  if (lv["h2"] == eN.bi.off) {
    assert(lv["h3"] == eN.bi.off);
  }
  if (lv["h3"] == eN.bi.off) {
  }
  if (lv["h4"] == eN.bi.off) {
    assert(lv["h5"] == eN.bi.off);
    assert(lv["h6"] == eN.bi.off);
    assert(lv["h7"] == eN.bi.off);
  }
  if (lv["h5"] == eN.bi.off) {
    assert(lv["h6"] == eN.bi.off);
    assert(lv["h7"] == eN.bi.off);
  }
  if (lv["h6"] == eN.bi.off) {
    assert(lv["h7"] == eN.bi.off);
  }
  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);
      assert(lv["h2"] == eN.bi.off);
      assert(lv["h3"] == eN.bi.off);
      assert(lv["h4"] == eN.bi.off);
      assert(lv["h5"] == eN.bi.off);
      assert(lv["h6"] == eN.bi.off);
      assert(lv["h7"] == eN.bi.off);
    } else {                       // (lv["h0"] > eN.bi.off)
      assert(lv["h0"] == eN.bi.off,"error should not enter level A a second time");
    }
    break;
  case "B":
    if (lv["h1"] == eN.bi.off) {
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h2"] == eN.bi.off);
      assert(lv["h3"] == eN.bi.off);
    } else {                       // (lv["h1"] > eN.bi.off)
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h1"] > eN.bi.off);
    }
    break;
  case "C":
    if (lv["h2"] == eN.bi.off) {
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h1"] > eN.bi.off);
      assert(lv["h3"] == eN.bi.off);
    } else {                       // (lv["h2"] > eN.bi.off)
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h1"] > eN.bi.off);
      assert(lv["h2"] > eN.bi.off);
    }
    break;
  case "D":
    if (lv["h3"] == eN.bi.off) {
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h1"] > eN.bi.off);
      assert(lv["h2"] > eN.bi.off);
    } else {                      // (lv["h3"] > eN.bi.off)
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h1"] > eN.bi.off);
      assert(lv["h2"] > eN.bi.off);
      assert(lv["h3"] > eN.bi.off);
    }
    break;
  case "1":
    if (lv["h4"] == eN.bi.off) {
      assert(lv["h0"] > eN.bi.off);
    } else {                      // (lv["h4"] > eN.bi.off)
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h4"] > eN.bi.off);
    }
    break;
  case "2":
    if (lv["h5"] == eN.bi.off) {
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h4"] > eN.bi.off);
    } else {                      // (lv["h5"] > eN.bi.off)
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h4"] > eN.bi.off);
      assert(lv["h5"] > eN.bi.off);
    }
    break;
  case "3":
    if (lv["h6"] == eN.bi.off) {
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h4"] > eN.bi.off);
      assert(lv["h5"] > eN.bi.off);
    } else {                      // (lv["h6"] > eN.bi.off)
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h4"] > eN.bi.off);
      assert(lv["h5"] > eN.bi.off);
      assert(lv["h6"] > eN.bi.off);
    }
    break;
  case "4":
    if (lv["h7"] == eN.bi.off) {
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h4"] > eN.bi.off);
      assert(lv["h5"] > eN.bi.off);
      assert(lv["h6"] > eN.bi.off);
    } else {                      // (lv["h7"] > eN.bi.off)
      assert(lv["h0"] > eN.bi.off);
      assert(lv["h4"] > eN.bi.off);
      assert(lv["h5"] > eN.bi.off);
      assert(lv["h6"] > eN.bi.off);
      assert(lv["h7"] > eN.bi.off);
    }
    break;
  default:
    break;
  }
}
#+END_SRC

*** doc sect keys seq

#+NAME: template_doc_sect_keys_seq
#+BEGIN_SRC d
template docSectKeysSeq() {
  @safe 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();
  }
}
#+END_SRC

* 2. Object Setter (Set Abstract Object) :module:spine:metadoc_object_setter:

set abstracted objects for downstream processing

** _module template_

#+HEADER: :tangle "../src/doc_reform/meta/metadoc_object_setter.d"
#+HEADER: :noweb yes
#+BEGIN_SRC d
<<doc_header_including_copyright_and_license>>
/++
  object setter:
  setting of sisu objects for downstream processing
  metadoc_object_setter.d
+/
module doc_reform.meta.metadoc_object_setter;
template ObjectSetter() {
  /+ structs +/
  <<meta_structs_init_docObj_metainfo>>
  <<meta_structs_init_docObj_txtAttrib>>
  <<meta_structs_init_docObj_has>>
  <<meta_structs_init_docObj_table>>
  <<meta_structs_init_docObj_code_block>>
  <<meta_structs_init_docObj_stow>>
  <<meta_structs_init_docObj_pointer>>
  <<meta_structs_init_docObj_tags>>
  <<meta_structs_init_docObj_composite>>
  <<meta_structs_init_docObj_theObjects>>
}
#+END_SRC

** 1. initialize structs :struct:
*** heading attribute

#+BEGIN_SRC d
struct HeadingAttrib {
  string lev                                    = "9";
  int    heading_lev_markup                     = 9;
  int    heading_lev_collapsed                  = 9;
  int[]  closes_lev_collapsed                   = [];
  int[]  closes_lev_markup                      = [];
  int    array_ptr                              = 0;
  int    heading_array_ptr_segments             = 0;
}
#+END_SRC

*** _composite object_ [#A]

#+NAME: meta_structs_init_docObj_metainfo
#+BEGIN_SRC d
struct DocObj_MetaInfo_ {
  string                 is_of_part                           = ""; // frontmatter, body, backmatter
  string                 is_of_section                        = ""; // toc, body, glossary, biography, book index, blurb
  string                 is_of_type                           = ""; // para, block ?
  string                 is_a                                 = ""; // heading, para, table, code block, group, verse/poem ...
  alias                  of_part                              = is_of_part;
  alias                  of_section                           = is_of_section;
  alias                  is_of                                = is_of_type;
  string                 attrib                               = "";
  string                 lang                                 = ""; // blocks: group, block, quote; not codeblock;
  string                 syntax                               = ""; // codeblock only
  /+ o_n +/
  int                    o_n_substantive                      = 0;
  int                    o_n_non_substantive                  = 0;
  int                    o_n_glossary                         = 0;
  int                    o_n_bibliography                     = 0;
  int                    o_n_book_index                       = 0;
  int                    o_n_blurb                            = 0;
  @safe  string object_number_substantive() const @property {
    return (o_n_substantive == 0) ? "" : o_n_substantive.to!string;
  }
  @safe string object_number_non_substantive() const @property {
    return (o_n_non_substantive == 0) ? "" : o_n_non_substantive.to!string;
  }
  @safe string object_number_glossary() const @property {
    return (o_n_glossary == 0) ? "" : o_n_glossary.to!string;
  }
  @safe string object_number_bibliography() const @property {
    return (o_n_bibliography == 0) ? "" : o_n_bibliography.to!string;
  }
  @safe string object_number_book_index() const @property {
    return (o_n_book_index == 0) ? "" : o_n_book_index.to!string;
  }
  @safe string object_number_blurb() const @property {
    return (o_n_blurb == 0) ? "" : o_n_blurb.to!string;
  }
  bool                   object_number_off                    = false;
  bool                   visible_object_number                = false;
  int                    object_number_type                   = 0; // { ocn, non, bkidx }
  /+ node +/
  string[string][string] node;
  int                    ocn                                  = 0;
  string                 identifier                           = "";
  @safe string object_number() const @property {
    return (ocn == 0) ? "" : ocn.to!string;
  }
  int                    o_n_type                             = 0;
  int                    heading_lev_markup                   = 9;
  int                    heading_lev_collapsed                = 9;
  @safe string marked_up_level() const @property {
    string _out;
    switch (heading_lev_markup) {
    case 0  : _out = "A"; break;
    case 1  : _out = "B"; break;
    case 2  : _out = "C"; break;
    case 3  : _out = "D"; break;
    case 4  : _out = "1"; break;
    case 5  : _out = "2"; break;
    case 6  : _out = "3"; break;
    case 7  : _out = "4"; break;
    default : _out = "";  break; // "9";
    }
    return _out;
  }
  bool                   dummy_heading                        = false;
  int[]                  markedup_ancestors                   = [ 0, 0, 0, 0, 0, 0, 0, 0,];
  int[]                  collapsed_ancestors                  = [ 0, 0, 0, 0, 0, 0, 0, 0,];
  int[]                  dom_structure_markedup_tags_status   = [ 0, 0, 0, 0, 0, 0, 0, 0,];
  int[]                  dom_structure_collapsed_tags_status  = [ 0, 0, 0, 0, 0, 0, 0, 0,];
  int                    parent_lev_markup                    = 0;
  int                    parent_ocn                           = 0;
  int                    last_decendant_ocn                   = 0;
}
#+END_SRC

**** object text attributes

#+NAME: meta_structs_init_docObj_txtAttrib
#+BEGIN_SRC d
struct DocObj_TxtAttrib_ {
  int                    indent_base                         = 0;
  int                    indent_hang                         = 0;
  bool                   bullet                              = false;
  string                 language                            = "";
}
#+END_SRC

**** object has within it

#+NAME: meta_structs_init_docObj_has
#+BEGIN_SRC d
struct DocObj_Has_ {
  bool                   inline_links                        = false;
  bool                   inline_notes_reg                    = false;
  bool                   inline_notes_star                   = false;
  bool                   images                              = false;
  bool                   image_without_dimensions            = false;
}
#+END_SRC

**** table attributes

#+NAME: meta_structs_init_docObj_table
#+BEGIN_SRC d
struct DocObj_Table_ {
  int                    number_of_columns                   = 0;
  double[]               column_widths                       = [];
  string[]               column_aligns                       = [];
  bool                   heading                             = false;
  bool                   walls                               = false;
}
#+END_SRC

**** code attributes

#+NAME: meta_structs_init_docObj_code_block
#+BEGIN_SRC d
struct DocObj_CodeBlock_ {
  string                 syntax                              = "";
  bool                   linenumbers                         = false;
}
#+END_SRC

**** stow (things to be protected from regular text transformations, so far links)

#+NAME: meta_structs_init_docObj_stow
#+BEGIN_SRC d
struct DocObj_Stow_ {
  string[]               link                               = [];
}
#+END_SRC

**** pointers

#+NAME: meta_structs_init_docObj_pointer
#+BEGIN_SRC d
struct DocObj_Pointer_ {
  int                    doc_object                          = 0;
  int                    html_segnames                       = 0;
  int                    heading                             = 0;
}
#+END_SRC

**** tags

#+NAME: meta_structs_init_docObj_tags
#+BEGIN_SRC d
struct DocObj_Tags_ {
  string[]               heading_ancestors_text              = [ "", "", "", "", "", "", "", "", ];
  string                 anchor_tag_html                     = "";
  string                 in_segment_html                     = "";
  string                 segment_anchor_tag_epub             = "";
  string                 html_segment_anchor_tag_is          = "";
  string                 epub_segment_anchor_tag_is          = "";
  string                 heading_lev_anchor_tag              = "";
  string                 segname_prev                        = "";
  string                 segname_next                        = "";
  string[]               lev4_subtoc                         = [];
  string[]               anchor_tags                         = [];
}
#+END_SRC

**** composite object the parts

#+NAME: meta_structs_init_docObj_composite
#+BEGIN_SRC d
struct ObjGenericComposite {
  string                 text                                = "";
  DocObj_MetaInfo_       metainfo;
  DocObj_TxtAttrib_      attrib;
  DocObj_Tags_           tags;
  DocObj_Has_            has;
  DocObj_Table_          table;
  DocObj_CodeBlock_      code_block;
  DocObj_Stow_           stow;
  DocObj_Pointer_        ptr;
}
#+END_SRC

*** The Objects: generic composite object array

#+NAME: meta_structs_init_docObj_theObjects
#+BEGIN_SRC d
struct TheObjects {
  ObjGenericComposite[] oca;
}
#+END_SRC

* document header including copyright & license

#+NAME: doc_header_including_copyright_and_license
#+BEGIN_SRC text
/+
- Name: 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 - 2023 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.doc_reform.org]
    [https://www.sisudoc.org]

  - Git
    [https://git.sisudoc.org/projects/?p=software/spine.git;a=summary]

+/
#+END_SRC

* __END__