-*- mode: org -*-
#+TITLE:       sisu abstraction
#+DESCRIPTION: documents - structuring, various output representations & search
#+FILETAGS:    :sisu:abstraction:
#+AUTHOR:      Ralph Amissah
#+EMAIL:       [[mailto:ralph.amissah@gmail.com][ralph.amissah@gmail.com]]
#+COPYRIGHT:   Copyright (C) 2015 - 2021 Ralph Amissah
#+LANGUAGE:    en
#+STARTUP:     content hideblocks hidestars noindent entitiespretty
#+OPTIONS:     H:3 num:nil toc:t \n:nil @:t ::t |:t ^:nil _:nil -:t f:t *:t <:t
#+PROPERTY:    header-args  :exports code
#+PROPERTY:    header-args+ :noweb yes
#+PROPERTY:    header-args+ :eval no
#+PROPERTY:    header-args+ :results no
#+PROPERTY:    header-args+ :cache no
#+PROPERTY:    header-args+ :padline no
#+PROPERTY:    header-args+ :mkdirp yes

* ao.rb
** ao.rb

#+HEADER: :tangle "../lib/sisu/ao.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO
  require_relative 'se'                                 # se.rb
    include SiSU_Env
  require_relative 'dp'                                 # dp.rb
    include SiSU_Param
  require_relative 'ao_doc_objects'                     # ao_doc_objects.rb
  require_relative 'ao_syntax'                          # ao_syntax.rb
    include SiSU_AO_Syntax
  require_relative 'ao_doc_str'                         # ao_doc_str.rb
  require_relative 'ao_appendices'                      # ao_appendices.rb
  require_relative 'ao_idx'                             # ao_idx.rb
  require_relative 'ao_numbering'                       # ao_numbering.rb
  require_relative 'ao_hash_digest'                     # ao_hash_digest.rb
  require_relative 'ao_endnotes'                        # ao_endnotes.rb
  require_relative 'ao_images'                          # ao_images.rb
  require_relative 'ao_metadata'                        # ao_metadata.rb
  require_relative 'ao_character_check'                 # ao_character_check.rb
  require_relative 'ao_misc_arrange'                    # ao_misc_arrange.rb
  require_relative 'ao_expand_insertions'               # ao_expand_insertions.rb
  require_relative 'ao_persist'                         # ao_persist.rb
  require_relative 'prog_text_translation'              # prog_text_translation.rb
  require_relative 'shared_sem'                         # shared_sem.rb
  class Instantiate < SiSU_Param::Parameters::Instructions
    def initialize
      @@flag_vocab=0
      @@line_mode=''
    end
  end
  class Source <Instantiate
    def initialize(opt,fnx=nil,process=:complete)
      @opt,@fnx,@process=opt,fnx,process
      @per ||=SiSU_AO_Persist::Persist.new.persist_init
      @per.fns ||=opt.fns
      fn_use=if fnx \
      and fnx =~/\.ss[tmi]$/
        fnx
      elsif opt.fns =~/\.ssm$/
        opt.fns + '.sst'
      else
        opt.fns
      end
      @make_fns=SiSU_Env::InfoFile.new(fn_use)
      @fnm=@make_fns.marshal.ao_metadata
      @fnc=@make_fns.marshal.ao_content
      @idx_sst=@make_fns.marshal.ao_idx_sst_rel_html_seg
      @idx_raw=@make_fns.marshal.ao_idx_sst_rel
      @idx_html=@make_fns.marshal.ao_idx_html
      @idx_xhtml=@make_fns.marshal.ao_idx_xhtml
      @map_nametags=@make_fns.marshal.ao_map_nametags
      @map_ocn_htmlseg=@make_fns.marshal.ao_map_ocn_htmlseg
      @env=SiSU_Env::InfoEnv.new
    end
    def read                                                                   #creates ao
      begin
        @per=SiSU_AO_Persist::Persist.new
        @per.ao_arr=[]
        @per.fns=(@fnx && @fnx =~/\.ss[tmi]$/) \
        ? @fnx
        : @opt.fns
        create_ao
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.selections,@per.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
        SiSU_AO_Persist::Persist.new.persist_init
        SiSU_AO::Instantiate.new
      end
    end
    def get                                                                    #reads ao, unless does not exist then creates first
      begin
        ao=[]
        unless @per.fns==@opt.fns \
        or @per.fns==@fnx
          @per.fns=(@fnx && @fnx =~/\.ss[tmi]$/) \
          ? @fnx
          : @opt.fns
          @per.ao_arr=[]
        end
        ao=(@per.ao_arr.empty?) \
        ? read_fnc
        : @per.ao_arr.dup
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.selections,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
        SiSU_AO::Instantiate.new
      end
    end
    def get_idx_sst                                                            #reads ao idx.sst, #unless does not exist then creates first
      begin
        ao=[]
        unless @per.fns==@opt.fns \
        or @per.fns==@fnx
          @per.fns=(@fnx && @fnx =~/\.ss[tmi]$/) \
          ? @fnx
          : @opt.fns
          @per.idx_arr_sst=[]
        end
        ao=(@per.idx_arr_sst.empty?) \
        ? read_idx_sst
        : @per.idx_arr_sst.dup #check
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.selections,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
        SiSU_AO::Instantiate.new
      end
    end
    def get_idx_raw
      begin
        ao=[]
        unless @per.fns==@opt.fns \
        or @per.fns==@fnx
          @per.fns=(@fnx && @fnx =~/\.ss[tmi]$/) \
          ? @fnx
          : @opt.fns
          @per.idx_arr_tex=[]
        end
        ao=(@per.idx_arr_tex.empty?) \
        ? read_idx_raw
        : @per.idx_arr_tex.dup #check
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.selections,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
        SiSU_AO::Instantiate.new
      end
    end
    def get_idx_html                                                           #reads ao idx.html, #unless does not exist then creates first
      begin
        ao=[]
        unless @per.fns==@opt.fns \
        or @per.fns==@fnx
          @per.fns=(@fnx && @fnx =~/\.ss[tmi]$/) \
          ? @fnx
          : @opt.fns
          @per.idx_arr_html=[]
        end
        ao=(@per.idx_arr_html.empty?) \
        ? read_idx_html
        : @per.idx_arr_html.dup
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.selections,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
        SiSU_AO::Instantiate.new
      end
    end
    def get_idx_xhtml                                                          #reads ao idx.xhtml, #unless does not exist then creates first
      begin
        ao=[]
        unless @per.fns==@opt.fns \
        or @per.fns==@fnx
          @per.fns=(@fnx && @fnx =~/\.ss[tmi]$/) \
          ? @fnx
          : @opt.fns
          @per.idx_arr_xhtml=[] #...
        end
        ao=(@per.idx_arr_xhtml.empty?) \
        ? read_idx_xhtml
        : @per.idx_arr_xhtml.dup
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.selections,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
        SiSU_AO::Instantiate.new
      end
    end
    def get_map_nametags                                                       #reads ao map.nametags, #unless does not exist then creates first
      begin
        ao=[]
        unless @per.fns==@opt.fns \
        or @per.fns==@fnx
          @per.fns=(@fnx && @fnx =~/\.ss[tmi]$/) \
          ? @fnx
          : @opt.fns
          @per.map_arr_nametags=[]
        end
        ao=(@per.map_arr_nametags.empty?) \
        ? read_map_nametags
        : @per.map_arr_nametags.dup
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.selections,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
        SiSU_AO::Instantiate.new
      end
    end
    def get_map_ocn_htmlseg                                                    #reads ao map.ocn_htmlseg, #unless does not exist then creates first
      begin
        ao=[]
        unless @per.fns==@opt.fns \
        or @per.fns==@fnx
          @per.fns=(@fnx && @fnx =~/\.ss[tmi]$/) \
          ? @fnx
          : @opt.fns
          @per.map_arr_ocn_htmlseg=[]
        end
        ao=(@per.map_arr_ocn_htmlseg.empty?) \
        ? read_map_ocn_htmlseg
        : @per.map_arr_ocn_htmlseg.dup
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.selections,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
        SiSU_AO::Instantiate.new
      end
    end
  protected
    def create_ao
      ao_array=[]
      fnp = @fnx ? "#{@opt.fno} #{@fnx}" : @opt.fno
      unless @opt.act[:quiet][:set]==:on
        tell=(@opt.act[:verbose][:set]==:on \
        || @opt.act[:verbose_plus][:set]==:on \
        || @opt.act[:maintenance][:set]==:on) \
        ? SiSU_Screen::Ansi.new(
            @opt.act[:color_state][:set],
            'Document Abstraction'
          )
        : SiSU_Screen::Ansi.new(
            @opt.act[:color_state][:set],
            'Document Abstraction',
            "[#{@opt.f_pth[:lng_is]}] #{fnp}"
          )
        tell.blue_title_hi
      end
      fn=(@fnx && @fnx =~/\.ss[tmi]$/) \
      ? @fnx
      : @opt.fns
      if @opt.fno =~/\.txz$/
        Dir.chdir(@opt.f_pth[:pth])
      end
      meta=file_array=@env.source_file_processing_array(fn)
      @md=SiSU_Param::Parameters::Instructions.new(meta,@opt).extract
      meta=nil
      ao=SiSU_AO::Make.new(fn,@md,file_array,@fnx,@process).song
      if (@opt.act[:verbose][:set]==:on \
      || @opt.act[:verbose_plus][:set]==:on \
      || @opt.act[:maintenance][:set]==:on)
        cf=SiSU_Env::CreateFile.new(fn)
        if (@opt.act[:verbose][:set]==:on \
        || @opt.act[:verbose_plus][:set]==:on)
            SiSU_Screen::Ansi.new(
            @opt.act[:color_state][:set],
            @opt.fns,
            "~meta/#{@opt.fns}.meta"
          ).output
        elsif @opt.act[:maintenance][:set]==:on
          SiSU_Screen::Ansi.new(
            @opt.act[:color_state][:set],
            "ao -> #{cf.meta}"
          ).txt_grey
        end
      end
      ao.each {|s| ao_array << s}
      if @opt.act[:maintenance][:set]==:on
        ao_array.each do |obj|
          if defined? obj.parent
            if defined? obj.ln
              if defined? obj.node
                puts %{#{obj.ln}: #{obj.ocn} : #{obj.parent} : #{obj.node} - #{obj.lc}}
              else
                puts %{#{obj.ln}: #{obj.ocn} : #{obj.parent}}
              end
            else
              if defined? obj.node
                puts %{   #{obj.ocn} : #{obj.parent} : #{obj.node} - #{obj.lc}}
              else
                puts %{   #{obj.ocn} : #{obj.parent}}
              end
            end
          end
        end
      end
      ao_array
    end
    def read_fnm
      ao=[]
      ao=(FileTest.file?(@fnm)) \
      ? (File.open(@fnm,'r:utf-8'){ |f| ao=Marshal.load(f)})
      : SiSU_AO::Source.new(@opt).create_ao
    end
    def read_fnc
      ao=[]
      ao=(FileTest.file?(@fnc)) \
      ? (File.open(@fnc,'r:utf-8'){ |f| ao=Marshal.load(f)})
      : SiSU_AO::Source.new(@opt,@fnx,@process).create_ao
    end
    def read_idx_sst
      m=[]
      m=(FileTest.file?(@idx_sst)) \
      ? (File.open(@idx_sst,'r:utf-8'){ |f| m=Marshal.load(f)})
      : nil
    end
    def read_idx_raw
      m=[]
      m=(FileTest.file?(@idx_raw)) \
      ? (File.open(@idx_raw,'r:utf-8'){ |f| m=Marshal.load(f)})
      : nil
    end
    def read_idx_html
      m=[]
      m=(FileTest.file?(@idx_html)) \
      ? (File.open(@idx_html,'r:utf-8'){ |f| m=Marshal.load(f)})
      : nil
    end
    def read_idx_xhtml
      m=[]
      m=(FileTest.file?(@idx_xhtml)) \
      ? (File.open(@idx_xhtml,'r:utf-8'){ |f| m=Marshal.load(f)})
      : nil
    end
    def read_map_nametags
      m=[]
      m=(FileTest.file?(@map_nametags)) \
      ? (File.open(@map_nametags,'r:utf-8'){ |f| m=Marshal.load(f)})
      : nil
    end
    def read_map_ocn_htmlseg
      m=[]
      m=(FileTest.file?(@map_ocn_htmlseg)) \
      ? (File.open(@map_ocn_htmlseg,'r:utf-8'){ |f| m=Marshal.load(f)})
      : nil
    end
  end
  class Output
    def initialize(fn,md,data)
      @fn,@md,@data=fn,md,data
      @cf=SiSU_Env::CreateFile.new(@fn)
      @make=SiSU_Env::InfoFile.new(@fn)
      @dir=SiSU_Env::InfoEnv.new(@fn)
    end
    def screen_dump(o)
      if defined? o.of
        print %{OF: #{o.of}; }
      end
      if defined? o.is
        print %{IS: #{o.is.to_s}; }
      end
      if defined? o.ocn
        print %{OCN: #{o.ocn}; }
      end
      if defined? o.node
        print %{NODE: #{o.node}; }
      end
      if defined? o.parent
        print %{Parent: #{o.parent}; }
      end
      if defined? o.obj and not o.obj.empty?
        puts %{\n#{o.obj}; }
      else "\n"
      end
    end
    def screen_print(t_o)
      if defined? t_o
        print ' ' + t_o.to_s
      end
    end
    def screen_output(data)
      data.each do |o|
        print o.class
        screen_print(o.ocn)
        screen_print(o.obj)
        puts "\n"
      end
    end
    def hard_output
      if @md.opt.act[:maintenance][:set]==:on
        filename_meta=@cf.metaverse.file_meta
        @data.each {|o| filename_meta.puts o.inspect.sub(/:0x[0-9a-f]{8}\s/,': ')} #to make diffing easier
        filename_txt=@cf.metaverse.file_txt
        @data.each do |o|
          if defined? o.ocn
            filename_txt.puts case o.is
            when :heading
              "[#{o.is.to_s} #{o.lv}~#{o.name} [#{o.ocn}]] #{o.obj}"
            else "[#{o.is.to_s} [#{o.ocn}]] #{o.obj}"
            end
          else
            filename_txt.puts case o.is
            when :meta
              "[m~#{o.tag}] #{o.obj}"
            else "[#{o.is.to_s}] #{o.obj}"
            end
          end
        end
        filename_debug=@cf.file_debug
        @data.each do |o|
          if defined? o.ocn
            case o.is
            when :heading
              filename_debug.puts
                "#{o.is.to_s} #{o.lv}~#{o.name} odv=#{o.odv} osp=#{o.osp} [#{o.ocn}] -->\n\t#{o.obj}"
            end
          end
        end
      else
        hard="#{@dir.processing_path.ao}/#{@md.fns}.meta"
        File.unlink(hard) if FileTest.file?(hard)
        hard="#{@dir.processing_path.ao}/#{@md.fns}.txt"
        File.unlink(hard) if FileTest.file?(hard)
        hard="#{@dir.processing_path.ao}/#{@md.fns}.debug.txt"
        File.unlink(hard) if FileTest.file?(hard)
      end
    end
    def make_marshal_content
      marshal_ao=@make.marshal.ao_content
      File.open(marshal_ao,'w'){|f| Marshal.dump(@data,f)} if @data.is_a?(Array)
    end
    def make_marshal_metadata
      marshal_ao=@make.marshal.ao_metadata
      File.open(marshal_ao,'w'){|f| Marshal.dump(@data,f)} if @data.is_a?(Array)
    end
    def idx_html_hard_output
      if @md.book_idx \
      and @md.opt.act[:maintenance][:set]==:on
        filename_meta=@cf.file_meta_idx_html
        if @data.is_a?(Array)
          @data.each {|s| p s.inspect + "\n" unless s.is_a?(String)}
          @data.each {|s| filename_meta.puts s.strip + "\n" unless s.strip.empty?}
        end
      else
        hard_idx_html="#{@dir.processing_path.ao}/#{@md.fns}.idx.html"
        File.unlink(hard_idx_html) if FileTest.file?(hard_idx_html)
      end
    end
    def make_marshal_idx_sst_html_seg
      marshal_ao=@make.marshal.ao_idx_sst_rel_html_seg
      File.open(marshal_ao,'w'){|f| Marshal.dump(@data,f)} \
        if @data.is_a?(Array)
    end
    def make_marshal_idx_sst_rel
      marshal_ao=@make.marshal.ao_idx_sst_rel
      File.open(marshal_ao,'w'){|f| Marshal.dump(@data,f)} \
        if @data.is_a?(Array)
    end
    def make_marshal_idx_html
      marshal_ao=@make.marshal.ao_idx_html
      File.open(marshal_ao,'w'){|f| Marshal.dump(@data,f)} \
        if @data.is_a?(Array)
    end
    def make_marshal_idx_xhtml
      marshal_ao=@make.marshal.ao_idx_xhtml
      File.open(marshal_ao,'w'){|f| Marshal.dump(@data,f)} \
        if @data.is_a?(Array)
    end
    def make_marshal_map_nametags
      marshal_ao=@make.marshal.ao_map_nametags
      File.open(marshal_ao,'w'){|f| Marshal.dump(@data,f)} \
        if @data.is_a?(Hash)
    end
    def make_marshal_map_name_ocn_htmlseg
      marshal_ao=@make.marshal.ao_map_ocn_htmlseg
      File.open(marshal_ao,'w'){|f| Marshal.dump(@data,f)} \
        if @data.is_a?(Hash)
    end
  end
  class Make
    def initialize(fn,md,data,fnx,process)
      @fn,@md,@data,@fnx,@process=fn,md,data,fnx,process
      @env=SiSU_Env::InfoEnv.new(@md.fns)
    end
    def reset
      @@flag_vocab=0
      @@line_mode=''
    end
    def song
      reset
      data_txt=@data
      data_txt=
        SiSU_AO_Insertions::Insertions.new(@md,data_txt).                  # ao_expand_insertions.rb
          expand_insertions?
      data_txt=
        SiSU_AO_MiscArrangeText::SI.new(@md,data_txt).                     # ao_misc_arrange.rb
          prepare_text
      data_obj,
        metadata,
        bibliography=
          SiSU_AO_DocumentStructureExtract::Build.new(@md,data_txt).       # ao_doc_str.rb
            identify_parts
      data_obj=
        SiSU_AO_Syntax::Markup.new(@md,data_obj,bibliography).songsheet    # ao_syntax.rb
      data_obj,
        endnote_array=
          SiSU_AO_CharacterCheck::Check.new(data_obj).                     # ao_character_check.rb
            character_check_and_oldstyle_endnote_array
      data_obj=
         SiSU_AO_Images::Images.new(@md,data_obj).images                   # ao_images.rb
      data_obj,
        tags_map,
        ocn_html_seg_map=
          SiSU_AO_Numbering::Numbering.new(@md,data_obj,@fnx,@process).    # ao_numbering.rb
            numbering_song
      data_obj,
        book_index_rel,
        book_index_rel_html_seg,
        html_idx,
        xhtml_idx=
          SiSU_AO_BookIndex::BookIndex.new(@md,data_obj,@env).             # ao_idx.rb
            indexing_song if @md.book_idx
      data_obj=
        SiSU_AO_Endnotes::Endnotes.new(@md,data_obj,endnote_array).        # ao_endnotes.rb
          endnotes
      outputdata=data_obj
      if (@md.opt.act[:ao][:set]==:on \
      || @md.opt.act[:maintenance][:set]==:on)
        SiSU_AO::Output.new(@fn,@md,outputdata).hard_output
        SiSU_AO::Output.new(@fn,@md,outputdata).make_marshal_content
        SiSU_AO::Output.new(@fn,@md,metadata).make_marshal_metadata
        SiSU_AO::Output.new(@fn,@md,html_idx).idx_html_hard_output
        SiSU_AO::Output.new(@fn,@md,book_index_rel_html_seg).make_marshal_idx_sst_html_seg
        SiSU_AO::Output.new(@fn,@md,book_index_rel).make_marshal_idx_sst_rel
        SiSU_AO::Output.new(@fn,@md,html_idx).make_marshal_idx_html
        SiSU_AO::Output.new(@fn,@md,xhtml_idx).make_marshal_idx_xhtml
        SiSU_AO::Output.new(@fn,@md,tags_map).make_marshal_map_nametags
        SiSU_AO::Output.new(@fn,@md,ocn_html_seg_map).make_marshal_map_name_ocn_htmlseg
      end
      reset
      outputdata
    end
  protected
  end
end
__END__
#+END_SRC

** ao_appendices.rb

#+HEADER: :tangle "../lib/sisu/ao_appendices.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_Appendices
  class Glossary
    def initialize(md,data)
      @md,@data=md,data
    end
    def glossary_extraction
      glossary=[]
      glossaryflag=false
      code_flag=false
      flag_code_curly=:not_code_curly
      flag_code_tics=:not_code_tics
      @data=@data.select do |t_o|
        if t_o =~/^code\{/
          flag_code_curly=:code_curly
        elsif t_o =~/^\}code/
          flag_code_curly=:not_code_curly
        elsif t_o =~/^``` code/
          flag_code_tics=:code_tics
        elsif flag_code_tics ==:code_tics \
        and t_o =~/^```/
          flag_code_tics=:not_code_tics
        end
        code_flag=if flag_code_curly==:code_curly \
        or flag_code_tics==:code_tics
          true
        else false
        end
        unless code_flag
          if @md.flag_glossary
            if t_o =~/^1~!glossary/
              glossaryflag = true
              next
            elsif t_o =~/^:?[B-D]~/
              next
            elsif t_o =~/^:?[B-D1]~/
              glossaryflag = false
              t_o
            elsif glossaryflag
              if t_o !~/\A%+ /
                glossary << t_o
                next
              else
                t_o
              end
            else t_o
            end
          else t_o
          end
        else t_o
        end
      end.compact
      [@data,glossary]
    end
  end
  class Bibliography
    def initialize(md,data)
      @md,@data=md,data
    end
    def sort_bibliography_array_by_deemed_author_year_title(bib)
      if bib
        bib.compact.sort_by do |c|
          [c[:deemed_author],c[:ymd],c[:title]]
        end
      end
    end
    def citation_in_prepared_bibliography(cite)
      @cite=cite
      def generic
        {
           is:         nil, # :book, :article, :magazine, :newspaper, :blog, :other
           author_raw: nil,
           author:     nil,
           author_arr: nil,
           editor_raw: nil,
           editor:     nil,
           editor_arr: nil,
           title:      nil,
           subtitle:   nil,
           fulltitle:  nil,
           language:   nil,
           trans:      nil,
           src:        nil,
           journal:    nil,
           in:         nil,
           volume:     nil,
           edition:    nil,
           year:       nil,
           place:      nil,
           publisher:  nil,
           url:        nil,
           pages:      nil,
           note:       nil,
          #format:     nil, #consider list of fields arranged with markup
           short_name: nil,
           id:         nil,
        }
      end
      def citation_metadata
        type=:generic
        if type
          citation=generic
          citeblock=@cite.split("\n")
          citeblock.select do |meta|
            case meta
            when /^((?:au|author):\s+)\S+/ #req
              citation[:author_raw]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:ti|title):\s+)\S+/ #req
              citation[:title]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:st|subtitle):\s+)\S+/
              citation[:subtitle]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:lng|language):\s+)\S+/
              citation[:language]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:edr?|editor):\s+)\S+/
              citation[:editor_raw]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:tr|trans(:?lator)?):\s+)\S+/
              citation[:editor_raw]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:pb|publisher):\s+)\S+/
              citation[:publisher]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:edn|edition):\s+)\S+/
              citation[:edition]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:yr|year):\s+)\S+/ #req?
              citation[:year]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:pl|publisher_state):\s+)\S+/
              citation[:place]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:jo|journal):\s+)\S+/ #req?
              citation[:journal]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:vol?|volume):\s+)\S+/
              citation[:volume]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:in):\s+)\S+/
              citation[:in]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:src):\s+)\S+/
              citation[:src]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:pg|pages?):\s+)\S+/
              citation[:pages]=/^#{$1}(.+)/.match(meta)[1]
            when /^(url:\s+)\S+/
              citation[:url]=/^#{$1}(.+)/.match(meta)[1]
            when /^(note:\s+)\S+/
              citation[:note]=/^#{$1}(.+)/.match(meta)[1]
            when /^((?:sn|shortname):\s+)\S+/ # substitution: (/#{id}/,"#{sn}")
              citation[:short_name]=/^#{$1}(.+)/.match(meta)[1]
            when /^(id:\s+)\S+/               # substitution: (/#{id}/,"#{sn}")
              citation[:id]=/^#{$1}(.+)/.match(meta)[1]
            end
          end
          if citation[:subtitle]
            citation[:fulltitle] = citation[:title] \
            + ' - ' \
            + citation[:subtitle]
          else
            citation[:fulltitle] = citation[:title]
          end
          if citation[:author_raw]
            citation[:author_arr]=citation[:author_raw].split(/;\s*/)
            citation[:author]=citation[:author_arr].map do |author|
              author.gsub(/(.+?),\s+(.+)/,'\2 \1').strip
            end.join(', ').strip
          end
          if citation[:editor_raw]
            citation[:editor_arr]=citation[:editor_raw].split(/;\s*/)
            citation[:editor]=citation[:editor_arr].map do |editor|
              editor.gsub(/(.+?),\s+(.+)/,'\2 \1').strip
            end.join(', ').strip
          end
          citation[:ymd]=if not citation[:year] =~/^[0-9]{4}/
            '9999'
          else citation[:year]
          end
          citation[:deemed_author]=if not citation[:author_raw] \
          and citation[:editor_raw]
            citation[:editor_arr][0]
          elsif citation[:author_raw]
            citation[:author_arr][0]
          else
            SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:fuchsia).
              warn('Citation needs an author or editor, title: "' \
              + citation[:title] + '"')
            '000'
          end
          unless citation[:short_name]
            citation[:short_name]=%{#{citation[:author]}, "#{citation[:title]}" (#{citation[:date]})}
          end
        end
        citation
      end
      self
    end
    def biblio_format
      def generic(c)
        cite=%{#{c[:author]}. /{"#{c[:fulltitle]}".}/}
        cite=(c[:journal]) \
        ? cite + %{ #{c[:journal]},}
        : cite
        cite=(c[:source]) \
        ? cite + %{ #{c[:source]},}
        : cite
        cite=(c[:in]) \
        ? cite + %{ in #{c[:in]},}
        : cite
        cite=(c[:volume]) \
        ? cite + %{ #{c[:volume]},}
        : cite
        cite=(c[:trans]) \
        ? cite + %{ trans. #{c[:trans]},}
        : cite
        cite=(c[:editor]) \
        ? cite + %{ ed. #{c[:editor]},}
        : cite
        cite=(c[:place]) \
        ? cite + %{ #{c[:place]},}
        : cite
        cite=(c[:publisher]) \
        ? cite + %{ #{c[:publisher]},}
        : cite
        cite=(c[:year]) \
        ? cite + %{ (#{c[:year]})}
        : cite
        cite=(c[:pages]) \
        ? cite + %{ #{c[:pages]}}
        : cite
        cite=(c[:url]) \
        ? cite + %{ #{c[:url]}}
        : cite
        cite=(c[:note]) \
        ? cite + %{ #{c[:note]}}
        : cite
        cite
      end
      def generic_editor(c)
        cite=%{#{c[:editor]} ed. /{"#{c[:fulltitle]}".}/}
        cite=(c[:journal]) \
        ? cite + %{ #{c[:journal]}, }
        : cite
        cite=(c[:source]) \
        ? cite + %{ #{c[:source]}, }
        : cite
        cite=(c[:in]) \
        ? cite + %{ in #{c[:in]},}
        : cite
        cite=(c[:volume]) \
        ? cite + %{ #{c[:volume]},}
        : cite
        cite=(c[:trans]) \
        ? cite + %{ trans. #{c[:trans]},}
        : cite
        cite=(c[:place]) \
        ? cite + %{ #{c[:place]},}
        : cite
        cite=(c[:publisher]) \
        ? cite + %{ #{c[:publisher]}}
        : cite
        cite=(c[:year]) \
        ? cite + %{ (#{c[:year]})}
        : cite
        cite=(c[:pages]) \
        ? cite + %{ #{c[:pages]}}
        : cite
        cite=(c[:url]) \
        ? cite + %{ #{c[:url]}}
        : cite
        cite=(c[:note]) \
        ? cite + %{ #{c[:note]}}
        : cite
        cite
      end
      self
    end
    def biblio_make(cite)
      if cite[:author]
        biblio_format.generic(cite)
      elsif cite[:editor]
        biblio_format.generic_editor(cite)
      else
        biblio_format.generic(cite)
      end
    end
    def biblio_extraction
      bibliography=[]
      biblioflag=false
      code_flag=false
      flag_code_curly=:not_code_curly
      flag_code_tics=:not_code_tics
      @data=@data.select do |t_o|
        if t_o =~/^code\{/
          flag_code_curly=:code_curly
        elsif t_o =~/^\}code/
          flag_code_curly=:not_code_curly
        elsif t_o =~/^``` code/
          flag_code_tics=:code_tics
        elsif flag_code_tics ==:code_tics \
        and t_o =~/^```/
          flag_code_tics=:not_code_tics
        end
        code_flag=if flag_code_curly==:code_curly \
        or flag_code_tics==:code_tics
          true
        else false
        end
        unless code_flag
          if @md.flag_auto_biblio
            if t_o =~/^1~!biblio(?:graphy)?/
              biblioflag = true
              t_o
            elsif t_o =~/^:?[B-D1]~/
              biblioflag = false
              t_o
            elsif biblioflag
              if t_o !~/\A%+ /
                bibliography << citation_in_prepared_bibliography(t_o).citation_metadata
                next
              else
                t_o
              end
            else t_o
            end
          elsif @md.flag_biblio
            if t_o =~/^1~!biblio(?:graphy)?/
              biblioflag = true
              next
            elsif t_o =~/^:?[B-D]~/
              next
            elsif t_o =~/^:?[B-D1]~/
              biblioflag = false
              t_o
            elsif biblioflag
              if t_o !~/\A%+ /
                bibliography << t_o
                next
              else
                t_o
              end
            else t_o
            end
          else t_o
          end
        else t_o
        end
      end.compact
      if @md.flag_auto_biblio \
      and bibliography.length > 0
        data_new=[]
        bib=sort_bibliography_array_by_deemed_author_year_title(bibliography)
        biblio_done=[]
        @data.select do |t_o|
          if t_o =~/^1~!biblio(?:graphy)?/
            bib.each do |c|
              d=c
              d.store(:obj, biblio_make(c))
              biblio_done << d
              #biblio_done << { obj: biblio_make(c), id: c[:id] }
            end
          else data_new << t_o
          end
        end
        @data=data_new
      end
      [@data,biblio_done]
    end
  end
  class Citations
    def initialize(md='',data='')
      @md,@data=md,data
      #@biblio=[]
    end
    def songsheet
      tuned_file,citations=citations_scan(@data)
      [tuned_file,citations]
    end
    def sort_bibliography_array_by_author_year(bib)
      bib.sort_by do |c|
        [c[:author_raw],c[:year]]
        #[c[:author_arr][0],c[:year],c[:title]]
      end
    end
    def citations_regex
      def pages_pattern
        %r{(?:[,.:]?\s+(?:p{1,2}\.?\s+)?(?:\d+--?\d+)[,.]?\s+)?}
      end
      def editor_pattern
        %r{(?<editor>(?:editor|edited by)\s+.+?)}
      end
      def year_pattern
        %r{[(\[]?(?<year>\d{4})[\])]?[.,]?}
      end
      def authors_year_title_publication_editor_pages
        /(?<authors>.+?)\s+#{year_pattern}\s+"(?<title>.+?)"\s+(?:#{Mx[:fa_italics_o]}|#{Mx[:srcrgx_italics_o]})(?<publication>.+?)(?:#{Mx[:fa_italics_c]}|#{Mx[:srcrgx_italics_c]})\s+#{editor_pattern}#{pages_pattern}/m # note ed. is usually edition rather than editor
      end
      def authors_title_publication_year_editor_pages
        /(?<authors>.+?)\s+"(?<title>.+?)"\s+(?:#{Mx[:fa_italics_o]}|#{Mx[:srcrgx_italics_o]})(?<publication>.+?)(?:#{Mx[:fa_italics_c]}|#{Mx[:srcrgx_italics_c]})\s+#{year_pattern}\s+#{editor_pattern}#{pages_pattern}/m # note ed. is usually edition rather than editor
      end
      def authors_title_publication_editor_year_pages ###
        /(?<authors>.+?)\s+"(?<title>.+?)"\s+(?:#{Mx[:fa_italics_o]}|#{Mx[:srcrgx_italics_o]})(?<publication>.+?)(?:#{Mx[:fa_italics_c]}|#{Mx[:srcrgx_italics_c]})\s+ed.\s+#{editor_pattern}#{year_pattern}#{pages_pattern}/m
 # note ed. is usually edition rather than editor
      end
      def authors_title_publication_editor_pages_year ###
        /(?<authors>.+?)\s+"(?<title>.+?)"\s+(?:#{Mx[:fa_italics_o]}|#{Mx[:srcrgx_italics_o]})(?<publication>.+?)(?:#{Mx[:fa_italics_c]}|#{Mx[:srcrgx_italics_c]})\s+#{editor_pattern}#{pages_pattern}#{year_pattern}/m # note ed. is usually edition rather than editor
      end
      def authors_year_title_publication_pages
        /(?<authors>.+?)\s+#{year_pattern}\s+"(?<title>.+?)"\s+(?:#{Mx[:fa_italics_o]}|#{Mx[:srcrgx_italics_o]})(?<publication>.+?)(?:#{Mx[:fa_italics_c]}|#{Mx[:srcrgx_italics_c]})[,.;]?#{pages_pattern}/m
      end
      def authors_title_publication_year_pages
        /(?<authors>.+?)\s+"(?<title>.+?)"\s+(?:#{Mx[:fa_italics_o]}|#{Mx[:srcrgx_italics_o]})(?<publication>.+?)(?:#{Mx[:fa_italics_c]}|#{Mx[:srcrgx_italics_c]})\s+#{year_pattern}\s+#{pages_pattern}/m
      end
      def authors_title_publication_pages_year ###
        /(?<authors>.+?)\s+"(?<title>.+?)"\s+(?:#{Mx[:fa_italics_o]}|#{Mx[:srcrgx_italics_o]})(?<publication>.+?)(?:#{Mx[:fa_italics_c]}|#{Mx[:srcrgx_italics_c]})#{pages_pattern}#{year_pattern}/m
      end
      def authors_year_publication_pages
        /(?<authors>.+?)\s+#{year_pattern}\s+(?:#{Mx[:fa_italics_o]}|#{Mx[:srcrgx_italics_o]})(?<publication>.+?)(?:#{Mx[:fa_italics_c]}|#{Mx[:srcrgx_italics_c]})#{pages_pattern}/m
      end
      def authors_publication_year_pages
        /(?<authors>.+?)\s+(?:#{Mx[:fa_italics_o]}|#{Mx[:srcrgx_italics_o]})(?<publication>.+?)(?:#{Mx[:fa_italics_c]}|#{Mx[:srcrgx_italics_c]})[,.;]?\s+(?<publisher>.+?)?#{year_pattern}#{pages_pattern}[.;]?/m
      end
      self
    end
    def authors?(citations)
      citations.each.map do |b|
        if b =~ /^.+\s+::.+?:$/
          c=/^(?<citation>.+?)\s+::(?<shortref>.+?):$/.match(b)
          {
            citation: c[:citation],
            shortref: c[:shortref],
            c[:shortref].to_s => c[:citation]
          }
        else { citation: b }
        end
      end
    end
    def long_and_short_ref?(citations) #could be useful, keep ... ectract shortref
      citations.each.map do |b|
        if b =~ /^.+\s+::.+?:$/
          c=/^(?<citation>.+?)\s+::(?<shortref>.+?):$/.match(b)
          {
            citation: c[:citation],
            shortref: c[:shortref],
            c[:shortref].to_s => c[:citation]
          }
        else { citation: b }
        end
      end
    end
    def citation_detail(citations) #could be useful, keep ... extract shortref
      bibahash=[]
      number=0
      missed=0
      citations.select do |b|
        z=if b =~citations_regex.authors_year_title_publication_editor_pages
          c=citations_regex.authors_year_title_publication_editor_pages.match(b)
          {
            is: :article,
            author_raw: c[:authors],
            year: c[:year],
            title: c[:title],
            publication: c[:publication],
            editor: c[:editor],
          }
        elsif b =~citations_regex.authors_title_publication_year_editor_pages
          c=citations_regex.authors_title_publication_year_editor_pages.match(b)
          {
            is: :article,
            author_raw: c[:authors],
            year: c[:year],
            title: c[:title],
            publication: c[:publication],
            editor: c[:editor],
          }
        elsif b =~citations_regex.authors_title_publication_editor_year_pages
          c=citations_regex.authors_title_publication_editor_year_pages.match(b)
          {
            is: :article,
            author_raw: c[:authors],
            year: c[:year],
            title: c[:title],
            publication: c[:publication],
            editor: c[:editor],
          }
        elsif b =~citations_regex.authors_title_publication_editor_pages_year
          c=citations_regex.authors_title_publication_editor_pages_year.match(b)
          {
            is: :article,
            author_raw: c[:authors],
            year: c[:year],
            title: c[:title],
            publication: c[:publication],
            editor: c[:editor],
          }
        elsif b =~citations_regex.authors_year_title_publication_pages
          c=citations_regex.authors_year_title_publication_pages.match(b)
          {
            is: :article,
            author_raw: c[:authors],
            year: c[:year],
            title: c[:title],
            publication: c[:publication],
          }
        elsif b =~citations_regex.authors_title_publication_year_pages
          c=citations_regex.authors_title_publication_year_pages.match(b)
          {
            is: :article,
            author_raw: c[:authors],
            year: c[:year],
            title: c[:title],
            publication: c[:publication],
          }
        elsif b =~citations_regex.authors_year_publication_pages
          c=citations_regex.authors_year_publication_pages.match(b)
          {
            is: :book,
            author_raw: c[:authors],
            year: c[:year],
            publication: c[:publication],
          }
        elsif b =~citations_regex.authors_publication_year_pages
          c=citations_regex.authors_publication_year_pages.match(b)
          {
            is: :book,
            author_raw: c[:authors],
            year: c[:year],
            publication: c[:publication],
          }
        else b
        end
        if not z.is_a?(NilClass) \
        and z.is_a?(Hash) \
        and z[:author_raw].length > 0
          z[:author_arr]=z[:author_raw].split(/;\s*/)
          z[:author]=z[:author_arr].map do |author|
            author.gsub(/(.+?),\s+(.+)/,'\2 \1').strip
          end.join(', ').strip
          if @md.opt.act[:verbose_plus][:set]==:on \
          || @md.opt.act[:maintenance][:set]==:on
            number +=1 if z.is_a?(Hash)
            missed +=1 if z.is_a?(String)
            (z.is_a?(Hash)) \
            ? (p '[' + number.to_s + '] ' + z.to_s)
            : (p '<' + missed.to_s + '> ' + z.to_s)
          end
        end
        bibahash << z if z.is_a?(Hash)
      end
      bibahash=sort_bibliography_array_by_author_year(bibahash.compact)
      bibahash
    end
    def citations_scan(data)
      citations=[]
      #short_ref=[]
      tuned_file = data.compact.select do |dob|
        if dob.is !=:meta \
        && dob.is !=:comment \
        && dob.is !=:code \
        && dob.is !=:table
          if dob.obj =~/\.:.+?:\./
            citations << dob.obj.scan(/\.:\s*(.+?)\s*:\./m)
            #short_ref << dob.obj.scan(/\.:\s+(.+?)\s+::([^:]+)::\./m) #look at later
            ##short_ref << dob.obj.scan(/\.:\s+(.+?)\s+::(.+?)::\./m) #look at later
            #short_ref << dob.obj.scan(/\.:\s*(.+?)\s*(::(.+?):)?:\./m) #look at later
            citations=citations.flatten.compact
            dob.obj=dob.obj.   #remove citations delimiter & helpers from text
              gsub(/\.:|:\./,'')
          end
        end
        dob if dob.is_a?(Object)
      end
      #bib=long_and_short_ref?(citations) #could be useful, keep ... extract shortref
      citations=citation_detail(citations)
      [tuned_file,citations]
    end
  end
end
__END__
#+END_SRC

** ao_character_check.rb

#+HEADER: :tangle "../lib/sisu/ao_character_check.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_CharacterCheck
  class Check
    def initialize(data)
      @data=data
      @comment='%'
      @endnote_array=[]
    end
    def character_check_and_oldstyle_endnote_array
      data=@data
      @endnote_array=[]
      endnote_no=1
      @tuned_file=data.select do |dob|
        unless dob.is ==:table
          dob.obj=dob.obj.strip.
            gsub(/^[{~}]\s*$/,'').
            gsub(/~#\s*/,"#{Mx[:pa_non_object_no_heading]}").
            gsub(/-#\s*/,"#{Mx[:pa_non_object_dummy_heading]}").
            gsub(/(#{Mx[:en_a_o]})\s*\s+/,'\1 ').
            gsub(/(~\{\s*)\s+/,'\1 ').
            gsub(/ \/\//,"#{Mx[:br_line]}").
            gsub(/<br>/,"#{Mx[:br_line]}").                #needed by xml, xhtml etc.
            gsub(/\t/,' ').
            gsub(/\342\200\231/u,"'"). #if dob =~/’/       #Avoid #&lsquo; &rsquo; #&ldquo; &rdquo;
            gsub(/\\copy(?:right)?\b/,'&#169;').
            gsub(/\\trademark\b|\\tm\b/,'&#174;')
          dob.obj=dob.obj + "\n"
          unless dob.is ==:code
            case dob.obj
            when /\^~/                                     #% Note must do this first (earlier loop) and then enter gathered data into ~^\d+
              sub_dob=dob.obj.dup
              @endnote_array << sub_dob.gsub(/\n/,'').
                gsub(/\^~\s+(.+)\s*/,
                  %{#{Mx[:en_a_o]}#{endnote_no} \\1 #{Mx[:en_a_c]}}).
                  strip
              endnote_no+=1
              dob=nil if dob.obj =~/\^~ .+/                #watch, removes 'binary' endnote now in endnote array for later insertion
            end
          end
        end
        dob if dob.is_a?(Object)
      end.flatten.compact
      [@tuned_file,@endnote_array]
    end
  end
end
#+END_SRC

** ao_composite.rb

#+HEADER: :tangle "../lib/sisu/ao_composite.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_Assemble
  require_relative 'se'                                 # se.rb
  require_relative 'utils_composite'                    # utils_composite.rb
  class RemoteImage
    def initialize
      @env=SiSU_Env::InfoEnv.new
    end
    def image(dir)
      images=[]
      images[0]=dir
      images
    end
    def download_images(images_info)
      path="#{@env.processing_path.processing}/external_document/image"
      FileUtils::mkdir_p(path) \
        unless FileTest.directory?(path)
      download_from=images_info.shift
      images_info.each do |i|
        image="#{path}/#{i}"
        imagefile=File.new(image,'w+')
        open("#{download_from}/#{i}") do |g|
          imagefile << g.read
        end
        imagefile.close
      end
      output_path="#{@env.path.webserv}/#{@env.path.base_markup_dir_stub}/_sisu/image_external"
      FileUtils::mkdir_p(output_path) \
        unless FileTest.directory?(output_path)
      SiSU_Env::SystemCall.new("#{path}/*",output_path,'q').rsync
    end
  end
  class Composite
    include SiSU_Composite_Doc_Utils # composite doc, .ssm, extract all related insert files, array of filenames test
    def initialize(opt)
      @opt=opt
      @env=SiSU_Env::InfoEnv.new
    end
    def read
      begin
        pwd=Dir.pwd
        Dir.chdir(@opt.f_pth[:pth])
        if @opt.fno =~/\S+?\.ssm$/
          SiSU_Screen::Ansi.new(
            @opt.act[:color_state][:set],
            'Composite Document',
            "[#{@opt.f_pth[:lng_is]}] #{@opt.fno}",
          ).grey_title_hi unless @opt.act[:quiet][:set]==:on
          composite_and_imported_filenames_array(@opt.fno) # composite doc, .ssm, extract all related insert files, array of filenames test
          assembled=loadfile(@opt.fno)
          write(assembled)
        end
        Dir.chdir(pwd)
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.selections.str,@opt.fns).
          location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
      end
    end
    def insert?(para)
      if para =~ /^<<\s+((?:https?|file):\/\/\S+?\.ss[it])$/ # and NetTest
        url($1.strip)
      elsif para =~/^<<\s+(\S+?\.ss[it])$/
        loadfilename=$1.strip
        insert_array=loadfile(loadfilename)
        file=insertion(loadfilename,insert_array)
        file[:prepared]
      else para
      end
    end
    def loadfile(loadfilename)
      begin
        if FileTest.file?(loadfilename)
          insert_array=IO.readlines(loadfilename,'')
          if loadfilename =~/\S+?\.ss[itm]$/
            if (@opt.act[:verbose][:set]==:on \
            || @opt.act[:verbose_plus][:set]==:on \
            || @opt.act[:maintenance][:set]==:on)
              SiSU_Screen::Ansi.new(
                @opt.act[:color_state][:set],
                'loading:',
                loadfilename,
              ).txt_grey
            end
            tuned_file=if loadfilename =~/\S+?\.ss[im]$/
              insert_array.each.map do |para|
                insert?(para)
              end
            elsif loadfilename =~/\S+?\.sst$/
              insert_array.each.map do |para|
                para
              end
            end.flatten.compact
          end
        end
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.selections.str,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
      end
    end
    def url(loadfilename)
      if loadfilename =~ /((?:https?|file):\/\/\S+?\.ss[it])$/ # and NetTest
        loadfilename=$1
        begin
          require 'uri'
          require 'open-uri'
          require 'pp'
        rescue LoadError
          SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:fuchsia).
            error('uri, open-uri or pp NOT FOUND (LoadError)')
        end
        insert=open(loadfilename)
        insert_array=insert.dup
        insert.close
        file=insertion(loadfilename,insert_array)
        file[:prepared]
      end
    end
    def write(assembled)
      assembled_file=File.new("#{@env.processing_path.composite_file}/#{@opt.fnb}.ssm.sst",'w+')
      assembled.each {|a| assembled_file << a }
      assembled_file.close
    end
    def download_images(download_from,images_array)
      path="#{@env.processing_path.processing}/external_document/image"
      FileUtils::mkdir_p(path) unless FileTest.directory?(path)
      images_array.each do |i|
        image="#{path}/#{i}"
        unless FileTest.exists?(image)
          imagefile=File.new(image,'w+')
          open("#{download_from}/#{i}") do |g|
            imagefile << g.read
          end
          imagefile.close
        end
      end
    end
    def insertion(fni,insert_array)
      file={ prepared: [], images: [] }
      rgx_image=/(?:^|[^_\\])\{\s*(\S+?\.(?:png|jpg|gif))/
      file[:prepared] << "\n% |#{fni}|@|^|>>ok\n\n"
      @code_flag=false
      insert_array.each do |i|
        @code_flag=if i =~/^code\{/ then true
        elsif i =~/^\}code/         then false
        else @code_flag
        end
        if not @code_flag \
        and i !~/^%+\s/
          i=i.
            gsub(/^([123]|:?[ABCD])~\? /,
              '% [conditional heading:] \1~ ')    #off conditional heading (consider syntax)
          if i =~/^@\S+?:/
            i=i.gsub(/\n/m,"\n%  ").
              gsub(/\n%\s+$/m,'').
              gsub(/^@\S+?:/m,"\n% [imported header:] ")                       #off imported headers
          end
        end
        file[:prepared] << i
        if i !~/^%+\s/ \
        and i =~rgx_image
          file[:images] << i.scan(rgx_image).uniq
        end
      end
      file[:prepared] << "\n% end import" << "\n\n"
      if file[:images].length > 0
        file[:images]=file[:images].flatten.uniq
        file[:images].delete_if {|x| x =~/https?:\/\// }
      end
      file
    end
  end
  class CompositeFileList
    def initialize(opt)
      @opt=opt
      @env=SiSU_Env::InfoEnv.new
    end
    def read
      begin
        @opt.fns=@opt.fns.gsub(/\.ssm\.sst$/,'.ssm') #FIX earlier, hub
        fns_array=IO.readlines(@opt.fns,'')
        insertions?(fns_array)
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.selections.str,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
      end
    end
    def insertions?(fns_array)
      tuned_file=[]
      SiSU_Screen::Ansi.new(
        @opt.act[:color_state][:set],
        'Composite Document',
        @opt.fno
      ).grey_title_hi unless @opt.act[:quiet][:set]==:on
      @ssm=[@opt.fns]
      fns_array.each do |para|
        if para =~/^<<\s+(\S+?\.ss[it])$/
          loadfilename=$1.strip
          if (@opt.act[:verbose][:set]==:on \
          || @opt.act[:verbose_plus][:set]==:on \
          || @opt.act[:maintenance][:set]==:on)
            SiSU_Screen::Ansi.new(
              @opt.act[:color_state][:set],
              'loading:',
              loadfilename,
            ).txt_grey
          end
          tuned_file << if loadfilename =~ /(?:https?|file):\/\/\S+?\.ss[it]$/
            @ssm << loadfilename
          elsif loadfilename =~ /\.ss[it]$/ \
          and FileTest.file?(loadfilename)
            @ssm << loadfilename
          else
            STDERR.puts %{SKIPPED processing file: [#{@opt.lng}] "#{@opt.fns}" it requires an invalid or non-existent file: "#{loadfilename}"}
            $process_document = :skip; break #remove this line to continue processing documents that have missing include files
            para
          end
        end
      end
      @ssm
    end
  end
end
__END__
#+END_SRC

** ao_doc_objects.rb

#+HEADER: :tangle "../lib/sisu/ao_doc_objects.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_DocumentStructure
  class Extract
    def extract(h,o)
      h ? h : o
    end
  end
  class ObjectMetadata
    attr_accessor :is,:of,:tags,:obj,:digest
    def initialize
      @tags={}
      @is=@tmp=@digest=nil
      @of=:meta
    end
    def metadata(tags)
      of      = @of                                                                 #Symbol, classification - group
      is      = :meta                                                               #Symbol, classification - specific type
      tags    = tags            || ((defined? o.tags)      ? o.tags        : {})    #String, metadata type/tag
      obj     = nil
      @of,@is,@tags,@obj=of,is,tags,obj
      self
    end
  end
  class ObjectMeta
    attr_accessor :obj,:is,:of,:tag,:digest,:tmp
    def initialize
      @is=@obj=@tag=@digest=@digest=@tmp=nil
      @of=:meta
    end
    def metadata(h,o=nil)
      of      = @of                                                                 #Symbol, classification - group
      is      = :meta                                                               #Symbol, classification - specific type
      tag     = h[:tag]         || ((defined? o.tag)       ? o.tag         : nil)   #String, metadata type/tag
      obj     = h[:obj]         || ((defined? o.obj)       ? o.obj         : nil)   #String, text content
      tmp     = h[:tmp]         || ((defined? o.tmp)       ? o.tmp         : nil)   #available for processing, empty after use
      digest  = h[:digest]      || ((defined? o.digest)    ? o.digest      : nil)   #hash digests, sha512, sha256 or md5
      @of,@is,@tag,@obj,@digest,@tmp=of,is,tag,obj,digest,tmp
      self
    end
  end
  class ObjectHeading
    attr_accessor :obj,:is,:tags,:of,:lv,:ln,:lc,:use_,:name,:idx,:ocn,:odv,:osp,:node,:parent,:ocn_,:note_,:autonum_,:digest,:tmp
    def initialize
      @of=:para
      @is=@obj=@lv=@ln=@lc=@use_=@name=@idx=@size=@ocn=@odv=@osp=@node=@parent=@ocn_=@note_=@autonum_=@digest=@tmp=nil
      @tags=[]
    end
    def heading_ln(lv)
      case lv
      when /A/ then 0
      when /B/ then 1
      when /C/ then 2
      when /D/ then 3
      when /1/ then 4
      when /2/ then 5
      when /3/ then 6
      when /4/ then 7
      when /5/ then 8
      when /6/ then 9
      end
    end
    def heading_lv(ln)
      case ln.to_s
      when /0/ then 'A'
      when /1/ then 'B'
      when /2/ then 'C'
      when /3/ then 'D'
      when /4/ then '1'
      when /5/ then '2'
      when /6/ then '3'
      when /7/ then '4'
      when /8/ then '5'
      when /9/ then '6'
      end
    end
    def heading(h,o=nil)
      if not h[:ln] \
      and (h[:lv] and h[:lv]=~/[1-6A-D]/)
        h[:ln]=heading_ln(h[:lv])
      elsif not h[:lv] \
      and (h[:ln] and h[:ln].to_s=~/[0-9]/)
        h[:lv]=heading_lv(h[:ln])
      end
      of      = @of                                                                 #Symbol, classification - group
      is      = :heading                                                            #Symbol, classification - specific type
      name    = h[:name]        || ((defined? o.name)      ? o.name        : nil)   #String, named object?
      tags    = h[:tags]        || ((defined? o.tags)      ? o.tags        : [])    #Array, associated object tags, names if any
      obj     = h[:obj]         || ((defined? o.obj)       ? o.obj         : nil)   #String, text content
      idx     = h[:idx]         || ((defined? o.idx)       ? o.idx         : nil)   #String, book index provided?
      ocn     = h[:ocn]         || ((defined? o.ocn)       ? o.ocn         : nil)   #Integer, sequential on substantive-content objects
      odv     = h[:odv]         || ((defined? o.odv)       ? o.odv         : nil)
      osp     = h[:osp]         || ((defined? o.osp)       ? o.osp         : nil)
      node    = h[:node]        || ((defined? o.node)      ? o.node        : nil)   #[Node relationship doc structure info]
      parent  = h[:parent]      || ((defined? o.parent)    ? o.parent      : nil)   #[Node parent]
      lv      = h[:lv]          || ((defined? o.lv)        ? o.lv          : nil)   #Alpha-numeric, document structure as used in markup, A-D then 1-6
      ln      = h[:ln]          || ((defined? o.ln)        ? o.ln          : nil)   #Integer, document structure level, for convenience in processing 1-9
      lc      = h[:lc]          || ((defined? o.lc)        ? o.lc          : nil)   #Integer, document structure collapsed level, convenience (collapse sisu's dual level document structure for markup with simple linear structure)
      use_    = if lv \
        and lv == '1'
          h[:use_]              || ((defined? o.use_)      ? o.use_        : :ok)
        elsif not lv.empty? \
        and lv =~ /[A-D2-3]/
          :ok
        else
           h[:use_]             || ((defined? o.use_)      ? o.use_        : :ok)
        end
      ocn_    = if h[:ocn_].nil?
                                   ((defined? o.ocn_)      ? o.ocn_        : true)  #Bool? no ocn, non-substantive content, do not include in toc #consider
        else                       h[:ocn_]
        end
      autonum_ = if h[:autonum_].nil?
                                   ((defined? o.autonum_)  ? o.autonum_    : true)  #Bool? auto-numbering if requested default on, false suppresses
        else                       h[:autonum_]
        end
      note_   = h[:note_]       || ((defined? o.note_)     ? o.note_       : false) #Bool, endnotes/footnotes? (processing optimization)
      digest  = h[:digest]      || ((defined? o.digest)    ? o.digest      : nil)   #hash digests, sha512, sha256 or md5
      tmp     = h[:tmp]         || ((defined? o.tmp)       ? o.tmp         : nil)   #available for processing, empty after use
      @of,@is,@lv,@ln,@lc,@name,@tags,@obj,@idx,@ocn,@odv,@osp,@node,@parent,@use_,@ocn_,@note_,@autonum_,@digest,@tmp=
      of, is, lv, ln, lc, name, tags, obj, idx, ocn, odv, osp, node, parent, use_, ocn_, note_, autonum_, digest, tmp
      self
    end
    def heading_insert(h,o=nil)
      heading(h,o=nil)
      @is     = :heading_insert                                                     #String, classification - specific type
      self
    end
  end
  class ObjectPara
    attr_accessor :obj,:is,:tags,:of,:name,:idx,:quote_,:bullet_,:indent,:hang,:ocn,:odv,:osp,:parent,:note_,:image_,:ocn_,:digest,:tmp
    def initialize
      @of=:para
      @is=@obj=@name=@idx=@quote_=@bullet_=@indent=@hang=@size=@ocn=@odv=@osp=@parent=@note_=@image_=@ocn_=@digest=@tmp=nil
      @tags=[]
    end
    def paragraph(h,o=nil)
      of      = @of                                                                 #Symbol, classification - group
      is      = :para                                                               #Symbol, classification - specific type
      name    = h[:name]        || ((defined? o.name)      ? o.name        : nil)   #String, named object?
      tags    = h[:tags]        || ((defined? o.tags)      ? o.tags        : [])    #Array, associated object tags, names if any
      obj     = h[:obj]         || ((defined? o.obj)       ? o.obj         : nil)   #String, text content
      idx     = h[:idx]         || ((defined? o.idx)       ? o.idx         : nil)   #String, book index provided?
      ocn     = h[:ocn]         || ((defined? o.ocn)       ? o.ocn         : nil)   #Integer, sequential on substantive-content objects
      odv     = h[:odv]         || ((defined? o.odv)       ? o.odv         : nil)
      osp     = h[:osp]         || ((defined? o.osp)       ? o.osp         : nil)
      parent  = h[:parent]      || ((defined? o.parent)    ? o.parent      : nil)   #[Node parent]
      indent  = h[:indent].to_s || ((defined? o.indent)    ? o.indent.to_s : nil)   #Integer, indent level
      hang    = h[:hang].to_s   || ((defined? o.hang)      ? o.hang.to_s   : nil)   #Integer, hanging indent level
      bullet_ = h[:bullet_]     || ((defined? o.bullet_)   ? o.bullet_     : false) #Bool, bulleted?
      quote_  = h[:quote_]      || ((defined? o.quote_)    ? o.quote_      : false) #Bool, quote (blockquote)?
      note_   = h[:note_]       || ((defined? o.note_)     ? o.note_       : false) #Bool, endnotes/footnotes? (processing optimization)
      image_  = h[:image_]      || ((defined? o.image_)    ? o.image_      : false) #Bool, images? (processing optimization)
      ocn_    = if h[:ocn_].nil?
                                   ((defined? o.ocn_)      ? o.ocn_        : true)  #Bool? no ocn, non-substantive content, do not include in toc #consider
      else         h[:ocn_]
      end
      digest  = h[:digest]      || ((defined? o.digest)    ? o.digest      : nil)   #hash digests, sha512, sha256 or md5
      tmp     = h[:tmp]         || ((defined? o.tmp)       ? o.tmp         : nil)   #available for processing, empty after use
      @of,@is,@name,@tags,@obj,@indent,@hang,@bullet_,@quote_,@idx,@ocn,@odv,@osp,@parent,@image_,@note_,@ocn_,@digest,@tmp=
      of, is, name, tags, obj, indent, hang, bullet_, quote_, idx, ocn, odv, osp, parent, image_, note_, ocn_, digest, tmp
      self
    end
    def docinfo(h,o=nil)
      of      = @of                                                                 #String, classification - group
      is      = :docinfo                                                            #String, classification - specific type
      name    = h[:name]        || ((defined? o.name)      ? o.name        : nil)   #String, named object?
      tags    = h[:tags]        || ((defined? o.tags)      ? o.tags        : nil)   #Array, associated object tags, names if any
      obj     = h[:obj]         || ((defined? o.obj)       ? o.obj         : nil)   #String, text content
      idx     = nil                                                                 #String, book index provided?
      ocn     = nil                                                                 #Integer, sequential on substantive-content objects
      odv     = h[:odv]         || ((defined? o.odv)       ? o.odv         : nil)
      osp     = h[:osp]         || ((defined? o.osp)       ? o.osp         : nil)
      parent  = h[:parent]      || ((defined? o.parent)    ? o.parent      : nil)   #[Node parent]
      indent  = nil                                                                 #Integer, indent level
      hang    = nil                                                                 #Integer, indent level
      bullet_ = false                                                               #Bool, bulleted?
      note_   = false                                                               #Bool, endnotes/footnotes? (processing optimization)
      image_  = h[:image_]      || ((defined? o.image_)    ? o.image_      : false) #Bool, images? (processing optimization)
      ocn_    = if h[:ocn_].nil?
                                   ((defined? o.ocn_)      ? o.ocn_        : true)  #Bool? no ocn, non-substantive content, do not include in toc #consider
      else                         h[:ocn_]
      end
      digest  = h[:digest]      || ((defined? o.digest)    ? o.digest      : nil)   #hash digests, sha512, sha256 or md5
      tmp     = h[:tmp]         || ((defined? o.tmp)       ? o.tmp         : nil)   #available for processing, empty after use
      @of,@is,@name,@tags,@obj,@indent,@hang,@bullet_,@idx,@ocn,@odv,@osp,@parent,@image_,@note_,@ocn_,@digest,@tmp=
      of, is, name, tags, obj, indent, hang, bullet_, idx, ocn, odv, osp, parent, image_, note_, ocn_, digest, tmp
      self
    end
  end
  class ObjectBlockTxt
    attr_accessor :obj,:is,:of,:tags,:lngsyn,:idx,:ocn,:odv,:osp,:parent,:note_,:number_,:ocn_,:digest,:tmp
    def initialize
      @of=:block
      @is=@obj=@lngsyn=@idx=@ocn=@odv=@osp=@parent=@note_=@number_=@ocn_=@digest=@tmp=nil
      @tags=[]
    end
    def code(h,o=nil)
      of       = @of                                                                #Symbol, classification - group #alt 'code'
      is       = :code                                                              #Symbol, classification - specific type
      tags     = h[:tags]       || ((defined? o.tags)      ? o.tags        : [])    #Array, associated object tags, names if any
      obj      = h[:obj]        || ((defined? o.obj)       ? o.obj         : nil)   #String, text content
      lngsyn   = h[:lngsyn]     || ((defined? o.lngsyn)    ? o.lngsyn      : :txt)  #symbol, code lngsyn
      idx      = h[:idx]        || ((defined? o.idx)       ? o.idx         : nil)   #String, book index provided?
      ocn      = h[:ocn]        || ((defined? o.ocn)       ? o.ocn         : nil)   #Integer, sequential on substantive-content objects
      odv      = h[:odv]        || ((defined? o.odv)       ? o.odv         : nil)
      osp      = h[:osp]        || ((defined? o.osp)       ? o.osp         : nil)
      parent   = h[:parent]     || ((defined? o.parent)    ? o.parent      : nil)   #[Node parent]
      number_  = h[:number_]    || ((defined? o.number_)   ? o.number_     : false) #Bool, numbered or not?
      note_    = h[:note_]      || ((defined? o.note_)     ? o.note_       : false) #Bool, endnotes/footnotes? (processing optimization)
      ocn_     = if h[:ocn_].nil?
                                   ((defined? o.ocn_)      ? o.ocn_        : true)  #Bool? no ocn, non-substantive content, do not include in toc #consider
      else                         h[:ocn_]
      end
      num      = h[:num]        || ((defined? o.num)       ? o.num         : nil)
      digest   = h[:digest]     || ((defined? o.digest)    ? o.digest      : nil)   #hash digests, sha512, sha256 or md5
      tmp      = h[:tmp]        || ((defined? o.tmp)       ? o.tmp         : nil)   #available for processing, empty after use
      @of,@is,@tags,@obj,@lngsyn,@idx,@ocn,@odv,@osp,@parent,@number_,@note_,@ocn_,@num,@digest,@tmp=
      of, is, tags, obj, lngsyn, idx, ocn, odv, osp, parent, number_, note_, ocn_, num, digest, tmp
      self
    end
    def box(h,o=nil)
      of       = @of                                                                #Symbol, classification - group
      is       = :box                                                               #Symbol, classification - specific type
      tags     = h[:tags]       || ((defined? o.tags)      ? o.tags        : [])    #Array, associated object tags, names if any
      obj      = h[:obj]        || ((defined? o.obj)       ? o.obj         : nil)   #String, text content
      idx      = h[:idx]        || ((defined? o.idx)       ? o.idx         : nil)   #String, book index provided?
      ocn      = h[:ocn]        || ((defined? o.ocn)       ? o.ocn         : nil)   #Integer, sequential on substantive-content objects
      odv      = h[:odv]        || ((defined? o.odv)       ? o.odv         : nil)
      osp      = h[:osp]        || ((defined? o.osp)       ? o.osp         : nil)
      parent   = h[:parent]     || ((defined? o.parent)    ? o.parent      : nil)   #[Node parent]
      note_    = h[:note_]      || ((defined? o.note_)     ? o.note_       : false) #Bool, endnotes/footnotes? (processing optimization)
      ocn_     = if h[:ocn_].nil?
                                   ((defined? o.ocn_)      ? o.ocn_        : true)  #Bool? no ocn, non-substantive content, do not include in toc #consider
      else                         h[:ocn_]
      end
      num      = h[:num]        || ((defined? o.num)       ? o.num         : nil)
      digest   = h[:digest]     || ((defined? o.digest)    ? o.digest      : nil)   #hash digests, sha512, sha256 or md5
      tmp      = h[:tmp]        || ((defined? o.tmp)       ? o.tmp         : nil)   #available for processing, empty after use
      @of,@is,@tags,@obj,@idx,@ocn,@odv,@osp,@parent,@note_,@ocn_,@num,@digest,@tmp=
      of, is, tags, obj, idx, ocn, odv, osp, parent, note_, ocn_, num, digest, tmp
      self
    end
    def block(h,o=nil)
      of       = @of                                                                #Symbol, classification - group
      is       = :block                                                             #Symbol, classification - specific type
      tags     = h[:tags]       || ((defined? o.tags)      ? o.tags        : [])    #Array, associated object tags, names if any
      obj      = h[:obj]        || ((defined? o.obj)       ? o.obj         : nil)   #String, text content
      idx      = h[:idx]        || ((defined? o.idx)       ? o.idx         : nil)   #String, book index provided?
      ocn      = h[:ocn]        || ((defined? o.ocn)       ? o.ocn         : nil)   #Integer, sequential on substantive-content objects
      odv      = h[:odv]        || ((defined? o.odv)       ? o.odv         : nil)
      osp      = h[:osp]        || ((defined? o.osp)       ? o.osp         : nil)
      parent   = h[:parent]     || ((defined? o.parent)    ? o.parent      : nil)   #[Node parent]
      note_    = h[:note_]      || ((defined? o.note_)     ? o.note_       : false) #Bool, endnotes/footnotes? (processing optimization)
      ocn_     = if h[:ocn_].nil?
                                   ((defined? o.ocn_)      ? o.ocn_        : true)  #Bool? no ocn, non-substantive content, do not include in toc #consider
      else                         h[:ocn_]
      end
      num      = h[:num]        || ((defined? o.num)       ? o.num         : nil)
      digest   = h[:digest]     || ((defined? o.digest)    ? o.digest      : nil)   #hash digests, sha512, sha256 or md5
      tmp      = h[:tmp]        || ((defined? o.tmp)       ? o.tmp         : nil)   #available for processing, empty after use
      @of,@is,@tags,@obj,@idx,@ocn,@odv,@osp,@parent,@note_,@ocn_,@num,@digest,@tmp=
      of, is, tags, obj, idx, ocn, odv, osp, parent, note_, ocn_, num, digest, tmp
      self
    end
    def group(h,o=nil)
      of       = @of                                                                #Symbol, classification - group
      is       = :group                                                             #Symbol, classification - specific type
      tags     = h[:tags]       || ((defined? o.tags)      ? o.tags        : [])    #Array, associated object tags, names if any
      obj      = h[:obj]        || ((defined? o.obj)       ? o.obj         : nil)   #String, text content
      idx      = h[:idx]        || ((defined? o.idx)       ? o.idx         : nil)   #String, book index provided?
      ocn      = h[:ocn]        || ((defined? o.ocn)       ? o.ocn         : nil)   #Integer, sequential on substantive-content objects
      odv      = h[:odv]        || ((defined? o.odv)       ? o.odv         : nil)
      osp      = h[:osp]        || ((defined? o.osp)       ? o.osp         : nil)
      parent   = h[:parent]     || ((defined? o.parent)    ? o.parent      : nil)   #[Node parent]
      note_    = h[:note_]      || ((defined? o.note_)     ? o.note_       : false) #Bool, endnotes/footnotes? (processing optimization)
      ocn_     = if h[:ocn_].nil?
                                   ((defined? o.ocn_)      ? o.ocn_        : true)  #Bool? no ocn, non-substantive content, do not include in toc #consider
      else          h[:ocn_]
      end
      num      = h[:num]        || ((defined? o.num)       ? o.num         : nil)
      digest   = h[:digest]     || ((defined? o.digest)    ? o.digest      : nil)   #hash digests, sha512, sha256 or md5
      tmp      = h[:tmp]        || ((defined? o.tmp)       ? o.tmp         : nil)   #available for processing, empty after use
      @of,@is,@tags,@obj,@idx,@ocn,@odv,@osp,@parent,@note_,@ocn_,@num,@digest,@tmp=
      of, is, tags, obj, idx, ocn, odv, osp, parent, note_, ocn_, num, digest, tmp
      self
    end
    def alt(h,o=nil)                                                                #see block
      of       = @of                                                                #Symbol, classification - group
      is       = :alt                                                               #Symbol, classification - specific type
      tags     = h[:tags]       || ((defined? o.tags)      ? o.tags        : [])    #Array, associated object tags, names if any
      obj      = h[:obj]        || ((defined? o.obj)       ? o.obj         : nil)   #String, text content
      idx      = h[:idx]        || ((defined? o.idx)       ? o.idx         : nil)   #String, book index provided?
      ocn      = h[:ocn]        || ((defined? o.ocn)       ? o.ocn         : nil)   #Integer, sequential on substantive-content objects
      odv      = h[:odv]        || ((defined? o.odv)       ? o.odv         : nil)
      osp      = h[:osp]        || ((defined? o.osp)       ? o.osp         : nil)
      parent   = h[:parent]     || ((defined? o.parent)    ? o.parent      : nil)   #[Node parent]
      note_    = h[:note_]      || ((defined? o.note_)     ? o.note_       : false) #Bool, endnotes/footnotes? (processing optimization)
      ocn_     = if h[:ocn_].nil?
                                   ((defined? o.ocn_)      ? o.ocn_        : true)  #Bool? no ocn, non-substantive content, do not include in toc #consider
      else                         h[:ocn_]
      end
      num      = h[:num]        || ((defined? o.num)       ? o.num         : nil)
      digest   = h[:digest]     || ((defined? o.digest)    ? o.digest      : nil)   #hash digests, sha512, sha256 or md5
      tmp      = h[:tmp]        || ((defined? o.tmp)       ? o.tmp         : nil)   #available for processing, empty after use
      @of,@is,@tags,@obj,@idx,@ocn,@odv,@osp,@parent,@note_,@ocn_,@num,@digest,@tmp=
      of, is, tags, obj, idx, ocn, odv, osp, parent, note_, ocn_, num, digest, tmp
      self
    end
    def verse(h,o=nil)                                                              #part of poem decide how you deal with this
      of       = @of                                                                #Symbol, classification - group
      is       = :verse                                                             #Symbol, classification - specific type
      tags     = h[:tags]       || ((defined? o.tags)      ? o.tags        : [])    #Array, associated object tags, names if any
      obj      = h[:obj]        || ((defined? o.obj)       ? o.obj         : nil)   #String, text content
      idx      = h[:idx]        || ((defined? o.idx)       ? o.idx         : nil)   #String, book index provided?
      ocn      = h[:ocn]        || ((defined? o.ocn)       ? o.ocn         : nil)   #Integer, sequential on substantive-content objects
      odv      = h[:odv]        || ((defined? o.odv)       ? o.odv         : nil)
      osp      = h[:osp]        || ((defined? o.osp)       ? o.osp         : nil)
      parent   = h[:parent]     || ((defined? o.parent)    ? o.parent      : nil)   #[Node parent]
      ocn_     = if h[:ocn_].nil?
                                   ((defined? o.ocn_)      ? o.ocn_        : true)  #Bool? no ocn, non-substantive content, do not include in toc #consider
      else                         h[:ocn_]
      end
      num      = h[:num]        || ((defined? o.num)       ? o.num         : nil)
      digest   = h[:digest]     || ((defined? o.digest)    ? o.digest      : nil)   #hash digests, sha512, sha256 or md5
      tmp      = h[:tmp]        || ((defined? o.tmp)       ? o.tmp         : nil)   #available for processing, empty after use
      @of,@is,@tags,@obj,@idx,@ocn,@odv,@osp,@parent,@note_,@ocn_,@num,@digest,@tmp=
      of, is, tags, obj, idx, ocn, odv, osp, parent, note_, ocn_, num, digest, tmp
      @h=nil
      self
    end
  end
  class ObjectTable
    attr_accessor :obj,:is,:of,:lv,:tags,:name,:idx,:indent,:hang,:size,:ocn,:num,:head_,:cols,:widths,:odv,:osp,:parent,:note_,:ocn_,:digest,:tmp
    def initialize
      @of=:block
      @is=@obj=@lv=@name=@idx=@indent=@hang=@size=@ocn,@num,@head_,@cols,@widths=@odv=@osp=@parent=@note_=@ocn_=@num=@digest=@tmp=nil
      @tags=[]
    end
    def table(h,o=nil)
      of      = @of                                                                 #Symbol, classification - group
      is      = :table                                                              #Symbol, classification - specific type
      tags    = h[:tags]        || ((defined? o.tags)      ? o.tags        : [])    #Array, associated object tags, names if any
      cols    = h[:cols]        || ((defined? o.cols)      ? o.cols        : nil)
      widths  = h[:widths]      || ((defined? o.widths)    ? o.widths      : nil)
      obj     = h[:obj]         || ((defined? o.obj)       ? o.obj         : nil)   #String, text content
      idx     = h[:idx]         || ((defined? o.idx)       ? o.idx         : nil)   #String, book index provided?
      ocn     = h[:ocn]         || ((defined? o.ocn)       ? o.ocn         : nil)   #Integer, sequential on substantive-content objects
      odv     = h[:odv]         || ((defined? o.odv)       ? o.odv         : nil)
      osp     = h[:osp]         || ((defined? o.osp)       ? o.osp         : nil)
      parent  = h[:parent]      || ((defined? o.parent)    ? o.parent      : nil)   #[Node parent]
      head_   = h[:head_]       || ((defined? o.head_)     ? o.head_       : false)
      note_   = h[:note_]       || ((defined? o.note_)     ? o.note_       : false) #Bool, endnotes/footnotes? (processing optimization)
      ocn_    = if h[:ocn_].nil?
                                   ((defined? o.ocn_)      ? o.ocn_        : true)  #Bool? no ocn, non-substantive content, do not include in toc #consider
      else                         h[:ocn_]
      end
      num     = h[:num]         || ((defined? o.num)       ? o.num         : nil)
      digest  = h[:digest]      || ((defined? o.digest)    ? o.digest      : nil)   #hash digests, sha512, sha256 or md5
      tmp     = h[:tmp]         || ((defined? o.tmp)       ? o.tmp         : nil)   #available for processing, empty after use
      @of,@is,@tags,@cols,@widths,@obj,@idx,@ocn,@odv,@osp,@parent,@head_,@note_,@ocn_,@num,@digest,@tmp=
      of, is, tags, cols, widths, obj, idx, ocn, odv, osp, parent, head_, note_, ocn_, num, digest, tmp
      self
    end
  end
  class ObjectImage
    attr_accessor :obj,:is,:of,:lv,:idx,:size,:ocn,:parent,:note_,:ocn_,:digest,:tmp
    def initialize
      @of=:image
      @is=@obj=@lv=@idx=@size=@ocn=@parent=@note_=@ocn_=@tmp=@digest=nil
      @tags=[]
    end
    def image(h,o=nil)                                                         #not yet used, and what of a paragraph containing several images, consider
      of=     @of                                                              #Symbol, classification - group
      is=     :image                                                           #Symbol, classification - specific type
      tags=   h[:tags]    || ((defined? o.tags)      ? o.tags    : [])         #Array, associated object tags, names if any
      obj=    h[:obj]     || ((defined? o.obj)       ? o.obj     : nil)        #String, text content
      size=   h[:size]    || ((defined? o.size)      ? o.size    : nil)
      idx=    h[:idx]     || ((defined? o.idx)       ? o.idx     : nil)        #String, book index provided?
      ocn=    h[:ocn]     || ((defined? o.ocn)       ? o.ocn     : nil)        #Integer, sequential on substantive-content objects
      odv=    h[:odv]     || ((defined? o.odv)       ? o.odv     : nil)
      osp=    h[:osp]     || ((defined? o.osp)       ? o.osp     : nil)
      parent= h[:parent]  || ((defined? o.parent)    ? o.parent  : nil)        #[Node parent]
      note_=  h[:note_]   || ((defined? o.note_)     ? o.note_   : false)      #Bool, endnotes/footnotes? (processing optimization)
      ocn_=if h[:ocn_].nil?
                             ((defined? o.ocn_)  ? o.ocn_    : true)           #Bool? no ocn, non-substantive content, do not include in toc #consider
      else                   h[:ocn_]
      end
      digest= h[:digest]  || ((defined? o.digest)    ? o.digest  : nil)        #hash digests, sha512, sha256 or md5
      tmp=    h[:tmp]     || ((defined? o.tmp)       ? o.tmp     : nil)        #available for processing, empty after use
      @of,@is,@tags,@obj,@size,@idx,@ocn,@odv,@osp,@parent,@note_,@ocn_,@digest,@tmp=of,is,tags,obj,size,idx,ocn,odv,osp,parent,note_,ocn_,digest,tmp
      self
    end
  end
  class ObjectStructure
    attr_accessor :obj,:tag,:node,:lv,:ln,:lc,:status,:is,:of,:tmp
    def initialize
      @of=:structure
      @is=@obj=@node=@lv=@ln=@lc=@status=@tmp=nil
    end
    def xml_dom(h,o=nil)
      of=     @of                                                              #Symbol, classification - group
      is=     :xml_dom                                                         #Symbol, classification - specific type
      obj=    h[:obj]     || ((defined? o.obj)       ? o.obj     : '')         #String, text content
      lv=     h[:lv]      || ((defined? o.lv)        ? o.lv      : nil)        #Alpha-numeric, document structure as used in markup, A-D then 1-6
      ln=     h[:ln]      || ((defined? o.ln)        ? o.ln      : nil)        #Integer, document structure level, for convenience in processing 1-9
      lc=     h[:lc]      || ((defined? o.lc)        ? o.lc      : nil)        #Integer, document structure collapsed level, convenience (collapse sisu's dual level document structure for markup with simple linear structure)
      node=   h[:node]    || ((defined? o.node)      ? o.node    : nil)        #[Node relationship doc structure info]
      status= h[:status]  || ((defined? o.status)    ? o.status  : nil)        #tag status Symbol :open or :close
      tmp=    h[:tmp]     || ((defined? o.tmp)       ? o.tmp     : nil)        #available for processing, empty after use
      @of,@is,@obj,@status,@node,@lv,@ln,@lc,@tmp=of,is,obj,status,node,lv,ln,lc,tmp
      self
    end
  end
  class ObjectFlag
    attr_accessor :obj,:is,:of,:flag,:act,:selections,:tmp
    def initialize
      @of=:flag
      @is=@obj=@flag=@act=@selections=@tmp=nil
    end
    def flag(h,o=nil)
      of=     @of                                                              #Symbol, classification - group
      is=     :flag                                                            #Symbol, classification - specific type
      obj=    nil                                                              #String, text content
      flag=   h[:flag]     || ((defined? o.flag)      ? o.flag    : nil)       #String, text content
      act=    h[:act]      || ((defined? o.act)       ? o.act     : nil)       #String, text content
      selections=    h[:selections]      || ((defined? o.selections) ? o.selections : nil)   #String, text content
      tmp=    h[:flag]     || ((defined? o.tmp)       ? o.tmp     : nil)       #available for processing, empty after use
      @of, @is,@obj,@flag,@act,@selections,@tmp=
        of,is, obj, flag, act, selections, tmp
      self
    end
    def flag_ocn(h,o=nil)
      of=     @of                                                              #Symbol, classification - group
      is=     :flag_ocn                                                        #Symbol, classification - specific type
      obj=    nil                                                              #String, text content
      flag=   h[:flag]     || ((defined? o.flag)      ? o.flag    : nil)       #String, text content
      act=    h[:act]      || ((defined? o.act)       ? o.act     : nil)       #String, text content
      selections= h[:selections] || ((defined? o.selections) ? o.selections : nil)   #String, text content
      tmp=    h[:flag]     || ((defined? o.tmp)       ? o.tmp     : nil)       #available for processing, empty after use
      @of, @is,@obj,@flag,@act,@selections,@tmp=
        of,is, obj, flag, act, selections,tmp
      self
    end
    def flag_lng(h,o=nil)
      of=     @of                                                              #Symbol, classification - group
      is=     :flag_lng
      obj=    nil                                                              #String, text content
      flag=   h[:flag]     || ((defined? o.flag)      ? o.flag    : nil)       #Symbol, :lng_on or :lng_off
      act=    h[:act]      || ((defined? o.act)       ? o.act     : nil)       #Symbol, language set to :en etc.
      selections= h[:selections] || ((defined? o.selections) ? o.selections : nil)   #String, text content
      tmp=    h[:act]     || ((defined? o.tmp)       ? o.tmp     : nil)       #available for processing, empty after use
      @of, @is,@obj,@flag,@act,@selections,@tmp=
        of,is, obj, flag, act, selections,tmp
      self
    end
  end
  class ObjectLayout
    attr_accessor :obj,:sym,:attr,:is,:is_for,:of,:from,:tmp,:num
    def initialize
      @of=:layout
      @is=@is_for=@obj=@from=@tmp=@num=nil
    end
    def break(h,f=nil)                                                         #decide how to deal with
      of=     @of                                                              #Symbol, classification - group
      is=     :break                                                           #Symbol, classification - specific type
      obj=    h[:obj]                                                          #String, text content
      from=   f
      tmp=    h[:tmp]                                                          #available for processing, empty after use
      @of,@is,@obj,@from,@tmp=of,is,obj,from,tmp
      self
    end
    def insert(h,o=nil)                                                        #decide how to deal with, could mimic paragraph?
      of=     @of                                                              #Symbol, classification - group
      is=     :insert                                                          #Symbol, classification - specific type
      obj=    h[:obj]     || ((defined? o.obj)       ? o.obj     : nil)        #String, text content
      tmp=    h[:tmp]     || ((defined? o.tmp)       ? o.tmp     : nil)        #available for processing, empty after use
      @of,@is,@obj,@tmp=of,is,obj,tmp
      self
    end
    def open_close(h,o=nil)                                                    #useful for poem & quote
      of=     @of                                                              #Symbol, classification - group
      is=     :open_close_tags                                                 #Symbol, classification - specific type
      is_for= h[:is_for]  || ((defined? o.is_for)    ? o.is_for  : nil)        #String, text content
      obj=    h[:obj]     || ((defined? o.obj)       ? o.obj     : nil)        #String, text content
      sym=    h[:sym]     || ((defined? o.sym)       ? o.sym     : nil)        #Symbol tag_open, tag_close
      attr=   h[:attr]    || ((defined? o.attr)      ? o.attr    : nil)        #String, text content
      tmp=    h[:tmp]     || ((defined? o.tmp)       ? o.tmp     : nil)        #available for processing, empty after use
      num=    h[:num]     || ((defined? o.num)       ? o.num     : nil)
      @of,@is,@is_for,@obj,@sym,@attr,@tmp,@num=
      of, is, is_for, obj, sym, attr, tmp, num
      self
    end
  end
  class ObjectComment
    attr_accessor :obj,:is,:of,:tmp
    def initialize
      @of=:comment
      @is=@obj=@tmp=nil
    end
    def comment(h,o=nil)
      of=     @of                                                              #Symbol, classification - group
      is=     :comment                                                         #Symbol, classification - specific type
      obj=    h[:obj]     || ((defined? o.obj)       ? o.obj     : nil)        #String, text content
      tmp=    h[:tmp]     || ((defined? o.tmp)       ? o.tmp     : nil)        #available for processing, empty after use
      @of,@is,@obj,@tmp=of,is,obj,tmp
      self
    end
  end
end
__END__
# ~# |-# no paragraph number # -# not included in toc
#+END_SRC

** ao_doc_str.rb

#+HEADER: :tangle "../lib/sisu/ao_doc_str.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_DocumentStructureExtract
  require_relative 'ao_persist'                     # ao_persist.rb
  class Instantiate < SiSU_Param::Parameters::Instructions
    def initialize
      @@counter=@@column=@@columns=0
      @@line_mode=''
    end
  end
  class Build
    def initialize(md,data)
      @md,@data=md,data
      SiSU_AO_DocumentStructureExtract::Instantiate.new
      @pb=SiSU_AO_DocumentStructure::ObjectLayout.new.break(Hx[:br_page])
      @pbn=SiSU_AO_DocumentStructure::ObjectLayout.new.break(Hx[:br_page_new])
      @pbl=SiSU_AO_DocumentStructure::ObjectLayout.new.break(Hx[:br_page_line])
      @per=SiSU_AO_Persist::PersistDocStructExt.new
      @make=SiSU_Env::ProcessingSettings.new(@md)
    end
    def ln_get(lv)
      case lv
      when /A/ then 0
      when /B/ then 1
      when /C/ then 2
      when /D/ then 3
      when /1/ then 4
      when /2/ then 5
      when /3/ then 6
      when /4/ then 7
      when /5/ then 8
      when /6/ then 9
      end
    end
    def image_test(str)
      str=~/\{\s*\S+?\.png.+?\}https?:\/\/\S+/ \
      ? true
      : false
    end
    def bullet_test(str)
      (str=~/\*/) \
      ? true
      : false
    end
    def quotes?
      @per.quote==:open \
      ? true
      : false
    end
    def hang_and_indent_test(str)
      hang_indent=if str=~/^_([1-9])[^_]/
        [$1,$1]
      elsif str=~/^__([1-9])/
        [0,$1]
      elsif str=~/^_([0-9])_([0-9])/
        [$1,$2]
      else
        [0,0]
      end
      hang,indent=hang_indent[0],hang_indent[1]
      [hang,indent]
    end
    def hang_and_indent_def_test(str1,str2)
      hang_indent=if str1=~/^_([1-9])[^_]/
        [$1,$1]
      elsif str1=~/^__([1-9])/
        [0,$1]
      elsif str1=~/^_([0-9])_([0-9])/
        [$1,$2]
      else
        [0,0]
      end
      obj=if str2 =~/^(.+?)\s+\\\\(?:\s+|\n)/
        str2.gsub(/^(.+?)(\s+\\\\(?:\s+|\n))/,
          "#{Mx[:fa_bold_o]}\\1#{Mx[:fa_bold_c]}\\2")
      else
        str2.gsub(/^(.+?)\n/,
          "#{Mx[:fa_bold_o]}\\1#{Mx[:fa_bold_c]}\n")
      end
      hang,indent=hang_indent[0],hang_indent[1]
      [
        hang,
        indent,
        obj,
      ]
    end
    def endnote_test?(str)
      (str=~/~\{.+?\}~|~\[.+?\]~/) \
      ? true
      : false
    end
    def extract_tags(str,nametag=nil)
      tags=[]
      if str.nil?
      else
        if str =~/(?:^|[ ])\*~([a-z0-9._-]+)(?=[ #{Mx[:br_nl]}]|$)/
          str=str.gsub(/(^|[ ])\*~([a-z0-9._-]+)(?=[ #{Mx[:br_nl]}]|$)/i,
              "\\1#{Mx[:tag_o]}\\2#{Mx[:tag_c]}").
            gsub(/ [ ]+/i,' ')
          tags=str.scan(/#{Mx[:tag_o]}(\S+?)#{Mx[:tag_c]}/).flatten.uniq
          str=str.gsub(/[ ]?#{Mx[:tag_o]}\S+?#{Mx[:tag_c]}[ ]?/,' ') #may be issues with spaces would leave one, but "code" blocks?
        end
        tags=nametag ? (tags << nametag) : tags
        tags.each do |t|
          t.gsub!(/[^a-z0-9._-]/,'')
        end
      end
      [
        str,
        tags,
      ]
    end
    def rgx_idx_ocn_seg
      @rgx_idx_ocn_seg=/(.+?)\s*[+](\d+)/
    end
    def construct_idx_array_and_hash(idxraw)
      idx_array_raw=idxraw.scan(/[^;]+/)
      idx_hash,idx_array,idx_lst={},[],[]
      idx_array_raw.each do |idx|
        idx=idx.strip
        idx_lst=case idx
        when /\S+?\s*:/
          idx_couplet_tmp=[]
          idx_couplet=idx.scan(/\s*[^:]+\s*/)
          if idx_couplet[1] =~/[|]/
            idx_couplet_tmp <<
              idx_couplet[0] <<
              idx_couplet[1].scan(/\s*[^|]+\s*/)
          else
            idx_couplet_tmp <<
              idx_couplet[0] <<
              [idx_couplet[1]]
          end
          idx_couplet=idx_couplet_tmp
        else [idx]
        end
        term_nodes=[]
        idx_lst.each do |term_node|
          case term_node
          when String
            term_node=
              term_node[0].chr.capitalize +
              term_node[1,term_node.length]
            term_node=(term_node =~/.+?[+]\d+/) \
            ? term_node
            : (term_node + '+0')
            term_nodes << term_node
            use,plus=rgx_idx_ocn_seg.match(term_node)[1,2]
            @use=use.strip
            unless idx_hash[@use] \
            and defined? idx_hash[@use]
              idx_hash[@use]=
                { sub: [], plus: plus }
            end
          when Array
            subterm_nodes=[]
            term_node.each do |subterm_node|
              subterm_node=(subterm_node =~/.+?[+]\d+/) \
              ? subterm_node
              : (subterm_node + '+0')
              subterm_nodes << subterm_node
              sub,sub_plus=rgx_idx_ocn_seg.match(subterm_node)[1,2]
              unless idx_hash[@use] \
              and defined? idx_hash[@use]
                idx_hash[@use]=
                  { sub: [], plus: 0 }
              end
              idx_hash[@use][:sub] <<
                { sub.strip => { plus: sub_plus } }
            end
            term_nodes << subterm_nodes
          end
        end
        idx_array << term_nodes
      end
      {
        hash: idx_hash,
        array: idx_array,
      }
    end
    def extract_structure_loop(data,tuned_file)
      data.each do |t_o|
        if t_o =~/^--([+~-])[#]$/
          h=case $1
          when /[+]/
            @per.ocn=:on
            {
              flag: :ocn_on,
            }
          when /[~]/
            @per.ocn=:ocn_off_headings_keep
            {
              flag: :ocn_off,
              mod: :headings_keep,
            }
          when /[-]/ #of particular relevance with level 1~ which is required to precede substantive text & used e.g. in html segmented text
            @per.ocn=:ocn_off_headings_dummy_lev1
            {
              flag: :ocn_off,
              mod: :headings_exclude,
            }
          else
            @per.ocn=:on
            {
              flag: :ocn_on,
            }
          end
          t_o=SiSU_AO_DocumentStructure::ObjectFlag.new.flag_ocn(h)
          next
        end
        if t_o =~/^:[~](#{SiSU_is.language_list_regex?}|-)$/  # work with for identifying language of objects
          lng=$1
          h=case lng
          when /(?:#{SiSU_is.language_list_regex?})/
            @per.lng=:on
            @per.lng_is=lng.to_sym
            {
              flag: :lng_on,
              act:  lng.to_sym,
            }
          else # ^:~-
            if @per.lng==:on
              @per.lng=:off
              @per.lng_is=:doc_default
              {
                flag: :lng_off,
                act:  :doc_default,
              }
            end
          end
          t_o=SiSU_AO_DocumentStructure::ObjectFlag.new.flag_lng(h)
          next
        end
        t_o=t_o.gsub(/(?:\n\s*\n)+/m,"\n") if @per.code==:off
        unless t_o =~/^(?:@\S+?:|%+)\s/                  # extract book index for paragraph if any
          idx=if t_o=~/^=\{\s*(.+)\s*\}\s*$\Z/m
            m=$1
            m=m.split(/[ ]*\n/).join(' ').
              gsub(/\s+([|:;])\s+/,'\1').
              gsub(/\s+([+]\d+)\s+/,'\1')
            t_o=t_o.gsub(/\n=\{.+?\}\s*$/m,'')
            idx_array_and_hash=construct_idx_array_and_hash(m)
            idx_array_and_hash[:hash]
          else nil
          end
        end
        if (t_o.is_a?(String) \
          && t_o !~/^(?:code(?:\.[a-z][0-9a-z_]+)?(?:\(.+?\))?|box(?:\.[a-z_]+)?|poem|alt|group|block)\{|^\}(?:code|poem|alt|group|block)|^(?:table\(.+?\)\{|\{table\()|^(?:table\{|\{table)[ ~]/ \
           && t_o !~/^```[ ]+(?:code(?:\.[a-z][0-9a-z_]+)?(?:\(.+?\))?|box(?:\.[a-z_]+)?|poem|alt|group|block|table)|^```(?:\s+[~-][#]|\s+\~\{.+?\}\~)?\s*$|^`:quote_(?:open|close)`/ \
          and @per.code==:off \
          and @per.poem==:off \
          and @per.group==:off \
          and @per.block==:off \
          and @per.alt==:off \
          and @per.box==:off \
          and @per.table==:off
        )
          t_o=case t_o
          when /^#{Mx[:meta_o]}\S+?#{Mx[:meta_c]}/                                 #metadata, header
            if t_o=~/^#{Mx[:meta_o]}(\S+?)#{Mx[:meta_c]}\s*(.+)/m
              tag,obj=$1,$2
              @metadata[tag]=obj
            end
            t_o=nil
          when /^%+\s/                                     #comment
            t_o=if t_o=~/^%+\s+(.+)/
              h={ obj: $1 }
              SiSU_AO_DocumentStructure::ObjectComment.new.comment(h)
            else nil
            end
          when /^:?([A-D1-6])\~/                           #heading / lv
            lv=$1
            ln=ln_get(lv)
            t_o=if t_o=~/^:?[A-D1-6]\~\s+(.+)/m
              obj=$1
              note=endnote_test?(obj)
              obj,tags=extract_tags(obj)
              if @per.ocn==:ocn_off_headings_dummy_lev1 \
              or @per.ocn==:ocn_off_headings_keep
                unless obj =~ /[~-][#]\s*$/
                  if @per.ocn==:ocn_off_headings_dummy_lev1 \
                  and t_o =~/^1\~\S*\s+/m
                    obj << ' -#'
                  elsif @per.ocn==:ocn_off_headings_dummy_lev1 \
                  or @per.ocn==:ocn_off_headings_keep
                    obj << ' ~#'
                  end
                end
              end
              h={
                lv: lv,
                ln: ln,
                obj: obj,
                idx: idx,
                tags: tags,
              }
              SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h)
            elsif t_o=~/^:?[A-D1-6]\~(\S+?)-\s+(.+)/m
              name,obj=$1,$2
              note=endnote_test?(obj)
              obj,tags=extract_tags(obj)
              if @per.ocn==:ocn_off_headings_dummy_lev1 \
              or @per.ocn==:ocn_off_headings_keep
                unless obj =~ /[~-][#]\s*$/
                  if @per.ocn==:ocn_off_headings_dummy_lev1 \
                  and t_o =~/^1\~\S*\s+/m
                    obj << ' -#'
                  elsif @per.ocn==:ocn_off_headings_dummy_lev1 \
                  or @per.ocn==:ocn_off_headings_keep
                    obj << ' ~#'
                  end
                end
              end
              h={
                lv: lv,
                name: name,
                obj: obj,
                idx: idx,
                autonum_: false,
                tags: tags,
              }
              SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h)
            elsif t_o=~/^:?[A-D1-6]\~(\S+)\s+(.+)/m
              name,obj=$1,$2
              note=endnote_test?(obj)
              obj,tags=extract_tags(obj,name)
              if @per.ocn==:ocn_off_headings_dummy_lev1 \
              or @per.ocn==:ocn_off_headings_keep
                unless obj =~ /[~-][#]\s*$/
                  if @per.ocn==:ocn_off_headings_dummy_lev1 \
                  and t_o =~/^1\~\S*\s+/m
                    obj << ' -#'
                  elsif @per.ocn==:ocn_off_headings_dummy_lev1 \
                  or @per.ocn==:ocn_off_headings_keep
                    obj << ' ~#'
                  end
                end
              end
              h={
                lv: lv,
                name: name,
                obj: obj,
                idx: idx,
                tags: tags,
              }
              SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h)
            else nil
            end
          when /^_(?:[1-9]!?|[1-9]?\*)\s+/                  #indented and/or bullet paragraph
            t_o=if t_o=~/^(_(?:[1-9]?\*|[1-9]!?)\s+)(.+)/m
              tst,obj=$1,$2
              if t_o=~/^_[1-9]!\s+.+/m
                hang,indent,obj=hang_and_indent_def_test(tst,obj)
              else
                hang,indent=hang_and_indent_test(tst)
              end
              bullet=bullet_test(tst)
              image=image_test(obj)
              note=endnote_test?(obj)
              obj,tags=extract_tags(obj)
              unless obj=~/\A\s*\Z/m
                if @per.ocn==:ocn_off_headings_dummy_lev1 \
                or @per.ocn==:ocn_off_headings_keep
                  unless obj =~ /[~-][#]\s*$/
                    obj << ' ~#'
                  end
                end
                h={
                  bullet_: bullet,
                  hang: hang,
                  indent: indent,
                  obj: obj,
                  idx: idx,
                  note_: note,
                  image_: image,
                  tags: tags,
                  quote: quotes?,
                }
                SiSU_AO_DocumentStructure::ObjectPara.new.paragraph(h)
              end
            else nil
            end
          when /^_[0-9]?_[0-9]!?\s+/                  #hanging indent paragraph
            t_o=if t_o=~/^(_[0-9]?_[0-9]!?\s+)(.+)/m
              tst,obj=$1,$2
              if t_o=~/^_[0-9]?_[0-9]!\s+.+/m
                hang,indent,obj=hang_and_indent_def_test(tst,obj)
              else
                hang,indent=hang_and_indent_test(tst)
              end
              image=image_test(obj)
              note=endnote_test?(obj)
              obj,tags=extract_tags(obj)
              unless obj=~/\A\s*\Z/m
                if @per.ocn==:ocn_off_headings_dummy_lev1 \
                or @per.ocn==:ocn_off_headings_keep
                  unless obj =~ /[~-][#]\s*$/
                    obj << ' ~#'
                  end
                end
                h={
                  hang: hang,
                  indent: indent,
                  obj: obj,
                  idx: idx,
                  note_: note,
                  image_: image,
                  tags: tags,
                  quote: quotes?,
                }
                SiSU_AO_DocumentStructure::ObjectPara.new.paragraph(h)
              end
            else nil
            end
          when /^<(?:br)?:(?:pa?r|o(?:bj|---)?)>\s*$/      #[br:par] #[br:obj]
            SiSU_AO_DocumentStructure::ObjectLayout.new.break(Hx[:br_obj])
          when /^(?:-\\\\-|<:pb>)\s*$/                                #[br:pg]
            SiSU_AO_DocumentStructure::ObjectLayout.new.break(Hx[:br_page],:markup)
          when /^(?:=\\\\=|<:pn>)\s*$/                                #[br:pgn]
            SiSU_AO_DocumentStructure::ObjectLayout.new.break(Hx[:br_page_new],:markup)
          when /^-\.\.-\s*$/                                          #[br:pgl]
            SiSU_AO_DocumentStructure::ObjectLayout.new.break(Hx[:br_page_line],:markup)
          else                                             #paragraph
            image=image_test(t_o)
            note=endnote_test?(t_o)
            obj,tags=extract_tags(t_o)
            if @per.ocn==:ocn_off_headings_dummy_lev1 \
            or @per.ocn==:ocn_off_headings_keep
              unless obj =~ /[~-][#]\s*$/
                obj << ' ~#'
              end
            end
            unless obj=~/\A\s*\Z/m
              h={
                bullet_: false,
                indent: 0,
                hang: 0,
                obj: obj,
                idx: idx,
                note_: note,
                image_: image,
                tags: tags,
                quote: quotes?,
              }
              t_o=SiSU_AO_DocumentStructure::ObjectPara.new.paragraph(h)
            end
            t_o=SiSU_AO_DocumentStructureExtract::Structure.new(@md).structure_markup(t_o) #must happen earlier, node info etc. require
          end
        elsif @per.code==:off
          if t_o =~/^(?:code(?:\.[a-z][0-9a-z_]+)?(?:\(.+?\))?\{|```[ ]+code(?:\.[a-z][0-9a-z_]+)?(?:\(.+?\))?)/
            @per.code=case t_o
            when /^code(?:\.[a-z][0-9a-z_]+)?(?:\(.+?\))?\{/ then :curls
            when /^```[ ]+code/                  then :tics
            else                                 @per.code #error
            end
            @per.lngsyn=if t_o =~/^(?:code\.[a-z][0-9a-z_]+(?:\(.+?\))?\{|```[ ]+code\.[a-z_]+)/
              case t_o
              when /^code\.([a-z][0-9a-z_]+)(?:\(.+?\))?\{/
                :"#{$1}"
              when /^```[ ]+code\.([a-z][0-9a-z_]+)/
                :"#{$1}"
              else :txt
              end
            else :txt
            end
            @@counter=1
            @codeblock_numbered=
              (t_o =~/^(?:code(?:\.[a-z][0-9a-z_]+)?\(.*number(?:lines)?.*?\)\{|```[ ]+code(?:\.[a-z][0-9a-z_]+)?\(.*number(?:lines)?.*?\)|code(?:\.[a-z][0-9a-z_]+)?\{#|```[ ]+code(?:\.[a-z][0-9a-z_]+)?\s[#])/) \
              ? true
              : false
            if (t_o =~/^(?:code(?:\.[a-z][0-9a-z_]+)?\{#|```[ ]+code(?:\.[a-z][0-9a-z_]+)?\s[#])/)
              puts "WARNING document using depreciated markup for numbering codeblocks\nuse: code(numberlines){ ... or: ```code(numberlines) ..."
            end
            @num_id[:code_block] +=1
            h={
              is_for: :code,
              obj: '',
              sym: :code_block_open,
              num: @num_id[:code_block],
              syntax: @per.lngsyn,
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
          elsif t_o =~/^(?:poem\{|```[ ]+poem)/
            @per.poem=case t_o
            when /^poem\{/        then :curls
            when /^```[ ]+poem/   then :tics
            else                  @per.poem #error
            end
            @num_id[:poem] +=1
            h={
              is_for: :poem,
              obj: '',
              sym: :poem_open,
              num: @num_id[:poem],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
            tuned_file << t_o
          elsif t_o =~/^(?:box(?:\.[a-z_]+)?\{|```[ ]+box(?:\.[a-z_]+)?)/
            @per.box=case t_o
            when /^box\{/         then :curls
            when /^```[ ]+box/    then :tics
            else                       @per.box #error
            end
            @num_id[:box] +=1
            h={
              is_for: :box,
              obj: '',
              sym: :box_open,
              num: @num_id[:box],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
            tuned_file << t_o
          elsif t_o =~/^(?:group\{|```[ ]+group)/
            @per.group=case t_o
            when /^group\{/       then :curls
            when /^```[ ]+group/  then :tics
            else                       @per.group #error
            end
            @num_id[:group] +=1
            h={
              is_for: :group,
              obj: '',
              sym: :group_open,
              num: @num_id[:group],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
            tuned_file << t_o
          elsif t_o =~/^(?:block\{|```[ ]+block)/
            @per.block=case t_o
            when /^block\{/       then :curls
            when /^```[ ]+block/  then :tics
            else                       @per.block #error
            end
            @num_id[:block] +=1
            h={
              is_for: :block,
              obj: '',
              sym: :block_open,
              num: @num_id[:block],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
            tuned_file << t_o
          elsif t_o =~/^(?:alt\{|```[ ]+alt)/
            @per.alt=case t_o
            when /^alt\{/         then :curls
            when /^```[ ]+alt/    then :tics
            else                       @per.alt #error
            end
            @num_id[:alt] +=1
            h={
              is_for: :alt,
              obj: '',
              sym: :alt_open,
              num: @num_id[:alt],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
            tuned_file << t_o
          elsif t_o =~/^`:quote_open`/
            @per.quote=:open
            @num_id[:quote] +=1
            h={
              is_for: :quote,
              obj: '',
              sym: :quote_open,
              num: @num_id[:quote],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
            #tuned_file << t_o #% find second source, entered twice, should be once so closed off here
          elsif t_o =~/^(?:table\(.+?\)\{|```[ ]+table\(.+?\)|\{table\(.+?\))/
            @num_id[:table] +=1
            h={
              is_for: :table,
              obj: '',
              sym: :table_open,
              num: @num_id[:table],
            }
            ins_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
            tuned_file << ins_o
            if t_o=~/^table\((?:.*?\bh;\s+)?.+?\)\{/
              @per.table=:curls
              @rows=''
              case t_o
              when /table\(.*?\bh;\s+c(\d+):\s+(.+?)\)\{/
                cols=$1
                col=$2.scan(/\d+/)
                heading=true
              when /table\(.*?c(\d+):\s+(.+?)\)\{/
                cols=$1
                col=$2.scan(/\d+/)
                heading=false
              end
              @h={
                head_: heading,
                cols: cols,
                widths: col,
                idx: idx,
              }
            elsif t_o=~/^```[ ]+table\((?:.*?\bh;)?\s+c\d+:/
              @per.table=:tics
              @rows=''
              case t_o
              when /^```[ ]+table\(.*?\bh;\s+c(\d+):\s+(.+?)\)/
                cols=$1
                col=$2.scan(/\d+/)
                heading=true
              when /^```[ ]+table\(\s*c(\d+):\s+(.+?)\)/
                cols=$1
                col=$2.scan(/\d+/)
                heading=false
              end
              @h={
                head_: heading,
                cols: cols,
                widths: col,
                idx: idx,
              }
            elsif t_o=~/^\{table\((?:.*?\bh;\s+)?(?:\s+\d+,?)?\)\s*\}\n.+\Z/m
              m1,m2,hd=nil,nil,nil
              tbl=/^\{table\((?:.*?\bh;\s+)?(?:\s+\d+,?)*\)\s*\}\n(.+)\Z/m.match(t_o)[1] # fix
              hd=((t_o =~/^\{table\(.*?\bh;\s+/) ? true : false)
              tbl,tags=extract_tags(tbl)
              rws=tbl.split(/\n/)
              rows=''
              cols=nil
              rws.each do |r|
                cols=(cols ? cols : (r.scan('|').length) +1)
                r=r.gsub(/\s*\|\s*/m,"#{Mx[:tc_p]}")       #r.gsub!(/\|/m,"#{Mx[:tc_p]}")
                rows += r + Mx[:tc_c]
              end
              col=[]
              if t_o =~/^\{table\((?:.*?\bh;\s+)?\s+c(\d+):.*?\)\s*\}/       #width of col 1 given as %, usually when wider than rest that are even
                c1=$1.to_i
                width=(100 - c1)/(cols - 1)
                col=[ c1 ]
                (cols - 1).times { col << width }
              else                                         #all columns of equal width
                width=100.00/cols
                cols.times { col << width }
              end
              h={
                head_: hd,
                cols: cols,
                widths: col,
                obj: rows,
                idx: idx,
                tags: tags,
                num: @num_id[:table],
              }
              t_o=SiSU_AO_DocumentStructure::ObjectTable.new.table(h) \
                unless h.nil?
              tuned_file << t_o
              h={
                is_for: :table,
                obj: '',
                sym: :table_close,
                num: @num_id[:table],
              }
              t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
              t_o
            elsif t_o=~/^```[ ]+table\((?:.*?\bh;)?\s+/
              m1,m2,hd=nil,nil,nil
              h=case t_o
              when /^```[ ]+table\(.*?\bh;\s+(.+?)\)\n(.+)\Z/m      #two table representations should be consolidated as one
                m1,tbl,hd=$1,$2,true
              when /^```[ ]+table\((.+?)\)\n(.+)\Z/m        #two table representations should be consolidated as one
                m1,tbl,hd=$1,$2,false
              else nil
              end
              tbl,tags=extract_tags(tbl)
              col=m1.scan(/\d+/)
              rws=tbl.split(/\n/)
              rows=''
              rws.each do |r|
                r=r.gsub(/\s*\|\s*/m,"#{Mx[:tc_p]}")       #r.gsub!(/\|/m,"#{Mx[:tc_p]}")
                rows += r + Mx[:tc_c]
              end
              h={
                head_: hd,
                cols: col.length,
                widths: col,
                obj: rows,
                idx: idx,
                tags: tags,
                num: @num_id[:table],
              }
              t_o=SiSU_AO_DocumentStructure::ObjectTable.new.table(h) \
                unless h.nil?
              tuned_file << t_o
              h={
                is_for: :table,
                obj: '',
                sym: :table_close,
                num: @num_id[:table],
                }
              t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
              t_o
            elsif t_o=~/^\{table\((?:.*?\bh;)?/
              m1,m2,hd=nil,nil,nil
              h=case t_o
              when /\{table\(.*?\bh;\s+(.+?)\)\s*\}\n(.+)\Z/m          #two table representations should be consolidated as one
                m1,tbl,hd=$1,$2,true
              when /\{table\((.+?)\)\s*\}\n(.+)\Z/m            #two table representations should be consolidated as one
                m1,tbl,hd=$1,$2,false
              else nil
              end
              tbl,tags=extract_tags(tbl)
              col=m1.scan(/\d+/)
              rws=tbl.split(/\n/)
              rows=''
              rws.each do |r|
                r=r.gsub(/\s*\|\s*/m,"#{Mx[:tc_p]}")       #r.gsub!(/\|/m,"#{Mx[:tc_p]}")
                rows += r + Mx[:tc_c]
              end
              h={
                head_: hd,
                cols: col.length,
                widths: col,
                obj: rows,
                idx: idx,
                tags: tags,
                num: @num_id[:table],
              }
              t_o=SiSU_AO_DocumentStructure::ObjectTable.new.table(h) \
                unless h.nil?
              tuned_file << t_o
              h={
                is_for: :table,
                obj: '',
                sym: :table_close,
                num: @num_id[:table],
              }
              t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
              t_o
            end
## depreciated markup, code should work for new markup after removal {
          elsif t_o =~/^(?:table\{|```[ ]+table|\{table)[ ~]/
            puts "WARNING document using depreciated markup for tables"
            puts "use table([table attributes]) instead:"
            puts "table(){"
            puts "``` table()"
            puts "{table()}"
            @num_id[:table] +=1
            h={
              is_for: :table,
              obj: '',
              sym: :table_open,
              num: @num_id[:table],
            }
            ins_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
            tuned_file << ins_o
            if t_o=~/^table\{(?:~h)?\s+/
              @per.table=:curls
              @rows=''
              case t_o
              when /table\{~h\s+c(\d+);\s+(.+)/
                cols=$1
                col=$2.scan(/\d+/)
                heading=true
              when /table\{\s+c(\d+);\s+(.+)/
                cols=$1
                col=$2.scan(/\d+/)
                heading=false
              end
              @h={
                head_: heading,
                cols: cols,
                widths: col,
                idx: idx,
              }
            elsif t_o=~/^```[ ]+table(?:~h)?\s+c\d+/
              @per.table=:tics
              @rows=''
              case t_o
              when /^```[ ]+table~h\s+c(\d+);\s+(.+)/
                cols=$1
                col=$2.scan(/\d+/)
                heading=true
              when /^```[ ]+table\s+c(\d+);\s+(.+)/
                cols=$1
                col=$2.scan(/\d+/)
                heading=false
              end
              @h={
                head_: heading,
                cols: cols,
                widths: col,
                idx: idx,
              }
            elsif t_o=~/^\{table(?:~h)?(?:\s+\d+;?)?\}\n.+\Z/m
              m1,m2,hd=nil,nil,nil
              tbl=/^\{table(?:~h)?(?:\s+\d+;?)?\}\n(.+)\Z/m.match(t_o)[1]
              hd=((t_o =~/^\{table~h/) ? true : false)
              tbl,tags=extract_tags(tbl)
              rws=tbl.split(/\n/)
              rows=''
              cols=nil
              rws.each do |r|
                cols=(cols ? cols : (r.scan('|').length) +1)
                r=r.gsub(/\s*\|\s*/m,"#{Mx[:tc_p]}")       #r.gsub!(/\|/m,"#{Mx[:tc_p]}")
                rows += r + Mx[:tc_c]
              end
              col=[]
              if t_o =~/^\{table(?:~h)?\s+(\d+);?\}/       #width of col 1 given as %, usually when wider than rest that are even
                c1=$1.to_i
                width=(100 - c1)/(cols - 1)
                col=[ c1 ]
                (cols - 1).times { col << width }
              else                                         #all columns of equal width
                width=100.00/cols
                cols.times { col << width }
              end
              h={
                head_: hd,
                cols: cols,
                widths: col,
                obj: rows,
                idx: idx,
                tags: tags,
                num: @num_id[:table],
              }
              t_o=SiSU_AO_DocumentStructure::ObjectTable.new.table(h) \
                unless h.nil?
              tuned_file << t_o
              h={
                is_for: :table,
                obj: '',
                sym: :table_close,
                num: @num_id[:table],
              }
              t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
              t_o
            elsif t_o=~/^```[ ]+table(?:~h)?\s+/
              m1,m2,hd=nil,nil,nil
              h=case t_o
              when /^```[ ]+table~h\s+(.+?)\n(.+)\Z/m      #two table representations should be consolidated as one
                m1,tbl,hd=$1,$2,true
              when /^```[ ]+table\s+(.+?)\n(.+)\Z/m        #two table representations should be consolidated as one
                m1,tbl,hd=$1,$2,false
              else nil
              end
              tbl,tags=extract_tags(tbl)
              col=m1.scan(/\d+/)
              rws=tbl.split(/\n/)
              rows=''
              rws.each do |r|
                r=r.gsub(/\s*\|\s*/m,"#{Mx[:tc_p]}")       #r.gsub!(/\|/m,"#{Mx[:tc_p]}")
                rows += r + Mx[:tc_c]
              end
              h={
                head_: hd,
                cols: col.length,
                widths: col,
                obj: rows,
                idx: idx,
                tags: tags,
                num: @num_id[:table],
              }
              t_o=SiSU_AO_DocumentStructure::ObjectTable.new.table(h) \
                unless h.nil?
              tuned_file << t_o
              h={
                is_for: :table,
                obj: '',
                sym: :table_close,
                num: @num_id[:table],
                }
              t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
              t_o
            elsif t_o=~/^\{table(?:~h)?\s+/
              m1,m2,hd=nil,nil,nil
              h=case t_o
              when /\{table~h\s+(.+?)\}\n(.+)\Z/m          #two table representations should be consolidated as one
                m1,tbl,hd=$1,$2,true
              when /\{table\s+(.+?)\}\n(.+)\Z/m            #two table representations should be consolidated as one
                m1,tbl,hd=$1,$2,false
              else nil
              end
              tbl,tags=extract_tags(tbl)
              col=m1.scan(/\d+/)
              rws=tbl.split(/\n/)
              rows=''
              rws.each do |r|
                r=r.gsub(/\s*\|\s*/m,"#{Mx[:tc_p]}")       #r.gsub!(/\|/m,"#{Mx[:tc_p]}")
                rows += r + Mx[:tc_c]
              end
              h={
                head_: hd,
                cols: col.length,
                widths: col,
                obj: rows,
                idx: idx,
                tags: tags,
                num: @num_id[:table],
              }
              t_o=SiSU_AO_DocumentStructure::ObjectTable.new.table(h) \
                unless h.nil?
              tuned_file << t_o
              h={
                is_for: :table,
                obj: '',
                sym: :table_close,
                num: @num_id[:table],
              }
              t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
              t_o
## } depreciated markup, code should (continue to) work for new markup after removal,
#    when removing depreciated markup check only pass-through for new table attributes format
#    table(.+?){  ``` table(.+?)  {table(.+?)} formats
            end
          end
          t_o
        end
        if @per.table==:curls or @per.table==:tics
          if (@per.table==:curls \
          and (t_o.is_a?(String) and t_o =~/^\}table/)) \
          or (@per.table==:tics \
          and (t_o.is_a?(String) and t_o =~/^```(?:\s+[~-][#]|\s+\~\{.+?\}\~)?\s*$/))
            @per.table=:off
            headings,columns,widths,idx=@h[:head_],@h[:cols],@h[:widths],@h[:idx]
            @h={
              head_: headings,
              cols: columns,
              widths: widths,
              idx: idx,
              obj: @rows,
            }
            t_o=SiSU_AO_DocumentStructure::ObjectTable.new.table(@h)
            tuned_file << t_o
            @h,@rows=nil,''
            h={
              is_for: :table,
              obj: '',
              sym: :table_close,
              num: @num_id[:table],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
            t_o
          else
            if t_o.is_a?(String) \
            and t_o !~/^(?:table\{|```[ ]+table)/
              t_o=t_o.gsub(/^\n+/m,'').
                gsub(/\n+/m,"#{Mx[:tc_p]}")
              @rows += t_o + Mx[:tc_c]
            end
            t_o=nil
          end
        end
        if @per.code==:curls \
        or @per.code==:tics
          if (@per.code==:curls \
          && (t_o.is_a?(String) && t_o =~/^\}code/)) \
          or (@per.code==:tics \
          && (t_o.is_a?(String) && t_o =~/^```(?:\s+[~-][#]|\s+\~\{.+?\}\~)?\s*$/m) \
          )
            @per.code=:off
            if @tuned_code[-1]
              @tuned_code[-1].
                gsub!(/\s*(?:#{Mx[:br_line]}|#{Mx[:br_nl]})\s*\Z/m,'')
            end
            obj=@tuned_code.join("\n")
            tags=[]
            h={
              obj: obj,
              idx: idx,
              syntax: @per.lngsyn,
              tags: tags,
              num: @num_id[:code_block],
              number_: @codeblock_numbered,
            }
            @per.lngsyn=:txt
            t_o=SiSU_AO_DocumentStructure::ObjectBlockTxt.new.code(h)
            @tuned_code=[]
            tuned_file << t_o
            h={
              is_for: :code,
              obj: '',
              sym: :code_close,
              num: @num_id[:code_block],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
          end
          if (@per.code==:curls \
          || @per.code==:tics) \
          and t_o.is_a?(String)
            sub_array=t_o.dup + "#{Mx[:br_nl]}"
            @line_mode=[]
            sub_array.scan(/.+/) {|w| @line_mode << w if w =~/[\S]+/}
            t_o=SiSU_AO_DocumentStructureExtract::Build.new(@md,@line_mode).build_lines(:code).join
            @tuned_code << t_o
            t_o=nil
          end
        elsif (@per.poem==:curls \
        || @per.poem==:tics) \
        or (@per.box==:curls \
        || @per.box==:tics) \
        or (@per.group==:curls \
        || @per.group==:tics) \
        or (@per.block==:curls \
        || @per.block==:tics) \
        or (@per.alt==:curls \
        || @per.alt==:tics) \
        or (@per.quote==:open \
        && (t_o.is_a?(String) && t_o =~/`:quote_close`/m))
          if (@per.poem==:curls \
          && (t_o.is_a?(String) && t_o.to_s =~/^\}poem$/m)) \
          or (@per.poem==:tics \
          && (t_o.is_a?(String) && t_o =~/^```(?:\s+[~-][#]|\s+\~\{.+?\}\~)?\s*$/))
            @per.poem=:off
            h={
              is_for: :poem,
              obj: '',
              idx: idx,
              sym: :poem_close,
              num: @num_id[:poem],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
          elsif (@per.box==:curls \
          && (t_o.is_a?(String) && t_o =~/^\}box/)) \
          or (@per.box==:tics \
          && (t_o.is_a?(String) && t_o.to_s =~/^```(?:\s+[~-][#]|\s+\~\{.+?\}\~)?\s*$/))
            @per.box=:off
            obj,tags=extract_tags(@tuned_block.join("\n"))
            h={
              obj: obj,
              idx: idx,
              tags: tags,
              num: @num_id[:box],
            }
            @tuned_block=[]
            t_o=SiSU_AO_DocumentStructure::ObjectBlockTxt.new.box(h)
            tuned_file << t_o
            h={
              is_for: :box,
              obj: '',
              idx: idx,
              sym: :box_close,
              num: @num_id[:box],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
          elsif (@per.group==:curls \
          && ( t_o.is_a?(String) && t_o.to_s =~/^\}group/)) \
          or (@per.group==:tics \
          && (t_o.is_a?(String) && t_o.to_s =~/^```(?:\s+[~-][#]|\s+\~\{.+?\}\~)?\s*$/))
            @per.group=:off
            obj,tags=extract_tags(@tuned_block.join("\n"))
            h={
              obj: obj,
              idx: idx,
              tags: tags,
              num: @num_id[:group],
            }
            @tuned_block=[]
            t_o=SiSU_AO_DocumentStructure::ObjectBlockTxt.new.group(h)
            tuned_file << t_o
            h={
              is_for: :group,
              obj: '',
              sym: :group_close,
              num: @num_id[:group],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
          elsif (@per.block==:curls \
          && t_o.to_s =~/^\}block/) \
          or (@per.block==:tics \
          && (t_o.is_a?(String) \
            && t_o =~/^```(?:\s+[~-][#]|\s+\~\{.+?\}\~)?\s*$/) \
          )
            @per.block=:off
            obj,tags=extract_tags(@tuned_block.join("\n"))
            h={
              obj: obj,
              idx: idx,
              tags: tags,
              num: @num_id[:block],
            }
            @tuned_block=[]
            t_o=SiSU_AO_DocumentStructure::ObjectBlockTxt.new.block(h)
            tuned_file << t_o
            h={
              is_for: :block,
              obj: '',
              sym: :block_close,
              num: @num_id[:block],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
          elsif (@per.alt==:curls \
          && (t_o.is_a?(String) && t_o =~/^\}alt/)) \
          or (@per.alt==:tics \
          && t_o.is_a?(String) \
            && (t_o =~/^```(?:\s+[~-][#]|\s+\~\{.+?\}\~)?\s*$/)
          )
            @per.alt=:off
            obj,tags=extract_tags(@tuned_block.join("\n"))
            h={
              obj: obj,
              idx: idx,
              tags: tags,
              num: @num_id[:alt],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectBlockTxt.new.alt(h)
            @tuned_block=[]
            tuned_file << t_o
            h={
              is_for: :alt,
              obj: '',
              sym: :alt_close,
              num: @num_id[:alt],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
          elsif @per.quote==:open \
          and (t_o.is_a?(String) &&  t_o =~/`:quote_close`/m)
            @per.quote=:off
            h={
              is_for: :quote,
              idx: idx,
              obj: '',
              sym: :quote_close,
              num: @num_id[:quote],
            }
            t_o=SiSU_AO_DocumentStructure::ObjectLayout.new.open_close(h)
          elsif @per.quote==:open
            t_o,tags=extract_tags(t_o)
            h={
              indent: 1,
              obj: t_o,
              idx: idx,
              note_: note,
              image_: image,
              tags: tags,
              quote: quotes?,
            }
            SiSU_AO_DocumentStructure::ObjectPara.new.paragraph(h)
          end
          if (@per.poem==:curls \
          || @per.poem==:tics) \
          or (@per.group==:curls \
          || @per.group==:tics) \
          or (@per.block==:curls \
          || @per.block==:tics) \
          or (@per.alt==:curls \
          || @per.alt==:tics) \
          and (t_o.is_a?(String) \
            and t_o.to_s =~/\S/ \
            and t_o.to_s !~/^(?:\}(?:verse|code|box|alt|group|block)|(?:verse|code(?:\.[a-z][0-9a-z_]+)?|box(?:\.[a-z_]+)?|alt|group|block)\{)/ \
            and t_o.to_s !~/^```[ ]+(?:code(?:\.[a-z][0-9a-z_]+)?|box(?:\.[a-z_]+)?|poem|alt|group|block)|^```(?:\s+[~-][#]|\s+\~\{.+?\}\~)?\s*$/
          )
            sub_array=t_o.to_s.dup
            @line_mode=sub_array.scan(/.+/)
            type=if @per.poem==:curls or @per.poem==:tics
              t_o=SiSU_AO_DocumentStructureExtract::Build.new(@md,@line_mode).build_lines(type).join
              poem=t_o.split(/\n\n/)
              poem.each do |v|
                v=v.gsub(/\n/m,"#{Mx[:br_nl]}\n")
                obj,tags=extract_tags(v)
                h={
                  obj: obj,
                  tags: tags,
                  num: @num_id[:poem],
                }
                t_o=SiSU_AO_DocumentStructure::ObjectBlockTxt.new.verse(h)
                tuned_file << t_o
              end
              :poem
            else :group
            end
          end
          @verse_count+=1 if @per.poem==:curls or @per.poem==:tics
        end
        if @per.code==:off
          if @per.poem==:curls or @per.poem==:tics \
          or @per.box==:curls or @per.box==:tics \
          or @per.group==:curls or @per.group==:tics \
          or @per.block==:curls or @per.block==:tics \
          or @per.alt==:curls or @per.alt==:tics \
          or (@per.quote==:open and t_o =~/`:quote_close`/m)
            if t_o.is_a?(String)
              t_o=t_o.gsub(/\n/m,"#{Mx[:br_nl]}").
                gsub(/[ ][ ]/m,"#{Mx[:nbsp]*2}").
                gsub(/#{Mx[:nbsp]}\s/,"#{Mx[:nbsp]*2}")
              t_o=t_o + Mx[:br_nl] if t_o =~/\S+/
            elsif t_o.is==:group \
            || t_o.is==:block \
            || t_o.is==:alt \
            || t_o.is==:box \
            || t_o.is==:verse
              t_o.obj=t_o.obj.gsub(/\n/m,"#{Mx[:br_nl]}").
                gsub(/[ ][ ]/m,"#{Mx[:nbsp]*2}").
                gsub(/#{Mx[:nbsp]}\s/,"#{Mx[:nbsp]*2}")
            end
            @tuned_block << t_o if t_o.to_s =~/\S+/
          else tuned_file << t_o
          end
        else tuned_file << t_o
        end
      end
      tuned_file
    end
    def identify_parts
      tuned_file=[]
      @tuned_block,@tuned_code=[],[]
      @@counter,@verse_count=0,0
      @num_id={
        code_block: 0,
        poem:       0,
        box:        0,
        block:      0,
        group:      0,
        alt:        0,
        quote:      0,
        table:      0,
      }
      @metadata={}
      if @md.flag_auto_biblio \
      or @md.flag_biblio
        @data,bibliography=SiSU_AO_Appendices::Bibliography.new(@md,@data).biblio_extraction
      end
      if @md.flag_glossary
        @data,glossary=SiSU_AO_Appendices::Glossary.new(@md,@data).glossary_extraction
      end
      tuned_file=extract_structure_loop(@data,tuned_file)
      if @md.flag_endnotes
        tuned_file << @pb
        h={
          ln: 1,
          lc: 1,
          obj: 'Endnotes',
          autonum_: false,
        }
        tuned_file << SiSU_AO_DocumentStructure::ObjectHeading.new.heading_insert(h)
        h={
          ln: 4,
          lc: 2,
          obj: 'Endnotes',
          name: 'endnotes',
          autonum_: false,
        }
        tuned_file << SiSU_AO_DocumentStructure::ObjectHeading.new.heading_insert(h)
        h={
          obj: 'Endnotes'
        }
      end
      if @md.flag_glossary
        tuned_file << @pb
        h={
          ln: 1,
          lc: 1,
          obj: 'Glossary',
          autonum_: false,
        }
        tuned_file << SiSU_AO_DocumentStructure::ObjectHeading.new.heading_insert(h)
        h={
          ln: 4,
          lc: 2,
          obj: 'Glossary',
          name: 'glossary',
          autonum_: false,
        }
        tuned_file << SiSU_AO_DocumentStructure::ObjectHeading.new.heading_insert(h)
        h={
          obj: 'Glossary'
        }
        if glossary.length > 0
          tuned_file=extract_structure_loop(glossary,tuned_file)
        end
      end
      if @md.flag_auto_biblio
        tuned_file << @pb
        h={
          ln: 1,
          lc: 1,
          obj: 'References',
          autonum_: false,
        }
        tuned_file << SiSU_AO_DocumentStructure::ObjectHeading.new.heading_insert(h)
        h={
          ln: 4,
          lc: 2,
          obj: 'Bibliography',
          name: 'biblio',
          autonum_: false,
        }
        tuned_file << SiSU_AO_DocumentStructure::ObjectHeading.new.heading_insert(h)
        h={
          obj: 'Bibliography'
        }
        citenumber=0
        bibliography.each do |cite|
          citenumber +=1 if cite.is_a?(Hash)
          h={
            obj: cite[:obj],
            #obj: %{[#{citenumber}] } + cite[:obj],
            tags: [cite[:id]],
            hang: 0,
            indent: 2,
            ocn_: false,
          }
          tuned_file << SiSU_AO_DocumentStructure::ObjectPara.new.paragraph(h)
        end
      elsif @md.flag_biblio
        tuned_file << @pb
        h={
          ln: 1,
          lc: 1,
          obj: 'References',
          autonum_: false,
        }
        tuned_file << SiSU_AO_DocumentStructure::ObjectHeading.new.heading_insert(h)
        h={
          ln: 4,
          lc: 2,
          obj: 'Bibliography',
          name: 'biblio',
          autonum_: false,
        }
        tuned_file << SiSU_AO_DocumentStructure::ObjectHeading.new.heading_insert(h)
        h={
          obj: 'Bibliography'
        }
        if not bibliography.nil? \
        and bibliography.length > 0
          tuned_file=extract_structure_loop(bibliography,tuned_file)
        else
          tuned_file, citations =
            SiSU_AO_Appendices::Citations.new(@md,tuned_file).songsheet  # ao_appendices.rb
          citenumber=0
          citations.compact.each do |c|
            citenumber +=1 if c.is_a?(Hash)
            if c[:is]==:book
              h={
                obj: %{#{c[:author]}. /{#{c[:publication]}}/ (#{c[:year]})},
                #obj: %{[#{citenumber}] *{#{c[:author]}}* /{#{c[:publication]}}/ (#{c[:year]})},
                hang: 0,
                indent: 2,
                ocn_: false,
              }
              tuned_file << SiSU_AO_DocumentStructure::ObjectPara.new.paragraph(h)
            elsif c[:is]==:article
              h={
                obj: %{#{c[:author]}. /{"#{c[:title]}"}/ #{c[:publication]} editor #{c[:editor]} (#{c[:year]})},
                #obj: %{[#{citenumber}] *{#{c[:author]}}* /{"#{c[:title]}"}/ #{c[:publication]} editor #{c[:editor]} (#{c[:year]})},
                hang: 0,
                indent: 2,
                ocn_: false,
              }
              tuned_file << SiSU_AO_DocumentStructure::ObjectPara.new.paragraph(h)
            end
          end
        end
      end
      if @md.book_idx
        tuned_file << @pb
        h={
          ln: 1,
          lc: 1,
          obj: 'Index',
          autonum_: false,
        }
        tuned_file << SiSU_AO_DocumentStructure::ObjectHeading.new.heading_insert(h)
        h={
          ln: 4,
          lc: 2,
          obj: 'Index',
          name: 'book_index',
          autonum_: false,
        }
        tuned_file << SiSU_AO_DocumentStructure::ObjectHeading.new.heading_insert(h)
        h={
          obj: 'Index'
        }
      end
      tuned_file << @pb
      if @make.build.metadata?
        h={
          ln: 1,
          lc: 1,
          obj: 'Metadata',
          autonum_: false,
          ocn_: false,
        }
        tuned_file << SiSU_AO_DocumentStructure::ObjectHeading.new.heading_insert(h)
        h={
          ln: 4,
          lc: 2,
          obj: 'SiSU Metadata, document information',
          name: 'metadata',
          autonum_: false,
          ocn_: false,
        }
        tuned_file << SiSU_AO_DocumentStructure::ObjectHeading.new.heading_insert(h)
      end
      h={
        obj: 'eof',
      }
      meta=SiSU_AO_DocumentStructure::ObjectMetadata.new.metadata(@metadata)
      [tuned_file,meta,bibliography,glossary]
    end
    def table_rows_and_columns_array(table_str)
      table=[]
      table_str.split(/#{Mx[:tc_c]}/).each do |table_row|
        table_row_with_columns=table_row.split(/#{Mx[:tc_p]}/)
        table << table_row_with_columns
      end
      table
    end
    def meta_heading(h)
      h={
        lv: h[:lv],
        ln: h[:ln],
        name: h[:name],
        obj: h[:obj],
        ocn: '0',
      }
      SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h)
    end
    def meta_para(str)
      h={
        obj: str,
        ocn_: false,
      }
      SiSU_AO_DocumentStructure::ObjectPara.new.paragraph(h)
    end
    def build_lines(type=:none)
      lines=@data
      lines.each.map do |line|
        line=if line =~/\S/ \
        and line !~/^(?:code(?:\.[a-z][0-9a-z_]+)?\{|\}code)/ \
        and line !~/^(?:```[ ]+code(?:\.[a-z][0-9a-z_]+)?|```(?:\s+[~-][#]|\s+\~\{.+?\}\~)?\s*$)/ \
        and not line.is_a?(Hash) #watch
          @@counter+=1 if @per.code==:curls or @per.code==:tics
          line=line.gsub(/\s\s/,"#{Mx[:nbsp]*2}").
            gsub(/#{Mx[:nbsp]}\s/,"#{Mx[:nbsp]*2}")
          line=line.gsub(/^/,"#{Mx[:gr_o]}codeline#{Mx[:gr_c]}") if type==:code # REMOVE try sort for texpdf special case
          line=if line =~/(?:https?|file|ftp):\/\/\S+$/
            line.gsub(/\s*$/," #{Mx[:br_nl]}")
          else line.gsub(/\s*$/,"#{Mx[:br_nl]}")           #unless type=='code'
          end
        elsif line =~/^\s*$/
          line.gsub(/\s*$/,"#{Mx[:br_nl]}")
        else line
        end
        line
      end
    end
  end
  class Structure                                          # this must happen early
    def initialize(md)
      @md=md
    end
    def structure(data)
      data.compact.each do |dob|
        structure_markup(dob)
      end
    end
    def structure_markup(dob)                                   #build structure where structure provided only in meta header
      dob=if dob.is==:para \
      && (((dob.hang !~/[1-9]/) && (dob.indent !~/[1-9]/)) \
      || (dob.hang != dob.indent)) \
      and not dob.bullet_
        dob=case dob.obj
        when /^#{@md.lv0}/
          h={
            is: :heading,
            lv: 'A',
            ln: 0,
          }
          SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h,dob)
        when /^#{@md.lv1}/
          h={
            is: :heading,
            lv: 'B',
            ln: 1,
          }
          SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h,dob)
        when /^#{@md.lv2}/
          h={
            is: :heading,
            lv: 'C',
            ln: 2,
          }
          SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h,dob)
        when /^#{@md.lv3}/
          h={
            is: :heading,
            lv: 'D',
            ln: 3,
          }
          SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h,dob)
        when /^#{@md.lv4}/
          h={
            is: :heading,
            lv: '1',
            ln: 4,
          }
          SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h,dob)
        when /^#{@md.lv5}/
          h={
            is: :heading,
            lv: '2',
            ln: 5,
          }
          SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h,dob)
        when /^#{@md.lv6}/
          h={
            is: :heading,
            lv: '3',
            ln: 6,
          }
          SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h,dob)
        else dob
        end
      else dob
      end
      dob
    end
  end
  class OCN
    def initialize(md,data,fnx,process)
      @md,@data,@fnx,@process=md,data,fnx,process
    end
    def structure_info
      def lv
        %w[A~ B~ C~ D~ 1 2 3 4]
      end
      def possible_parents(child)
        case child
        when /A~/ then 'none'
        when /B~/ then 'A~'
        when /C~/ then 'B~'
        when /D~/ then 'C~'
        when /1/  then 'A~, B~, C~, D~'
        when /2/  then '1'
        when /3/  then '2'
        when /4/  then '3'
        end
      end
      def possible_children(parent)
        case parent
        when /A~/ then 'B~, 1'
        when /B~/ then 'C~, 1'
        when /C~/ then 'D~, 1'
        when /D~/ then '1'
        when /1/  then '2'
        when /2/  then '3'
        when /3/  then '4'
        when /4/  then 'none'
        end
      end
      self
    end
    def document_structure_check_info(node,node_parent,status=:ok)
      node_ln=/^([0-7])/.match(node)[1].to_i
      node_parent_ln=/^([0-7])/.match(node_parent)[1].to_i
      if status==:error \
      or @md.opt.act[:maintenance][:set]==:on
        puts %{node: #{node},    parent node: #{node_parent}  #{status.upcase}}
        if status==:error
          node_ln=/^([0-7])/.match(node)[1].to_i
          node_parent_ln=/^([0-7])/.match(node_parent)[1].to_i
          STDERR.puts %{current level: #{structure_info.lv[node_ln]} (possible parent levels: #{structure_info.possible_parents(structure_info.lv[node_ln])})
parent level:  #{structure_info.lv[node_parent_ln]} (possible child levels: #{structure_info.possible_children(structure_info.lv[node_parent_ln])})
SKIPPED processing file:
[#{@md.opt.lng}] "#{@md.fns}"}
          if @md.opt.act[:no_stop][:set]==:on
            $process_document = :skip
          else exit
          end
        end
      end
    end
    def warning_incorrect_parent_level_or_level(txt)
      puts %{ERROR. There is an error in markup of heading levels either here or in the parent heading.
The current header reads:
"#{txt}"
has incorrect level and/or parent level
--}
    end
    def required_headers_present?
      if @process == :complete
        unless (defined? @md.title \
        and @md.title.full)
           STDERR.puts %{required header missing:

@title:
SKIPPED processing file:
[#{@md.opt.lng}] "#{@md.fns}"
}
          if @md.opt.act[:no_stop][:set]==:on
            $process_document = :skip
          else exit
          end
        end
        unless (defined? @md.creator.author \
        and @md.creator.author)
           STDERR.puts %{required header missing:

@creator:
 :author: anonymous?
SKIPPED processing file:
[#{@md.opt.lng}] "#{@md.fns}"
}
          if @md.opt.act[:no_stop][:set]==:on
            $process_document = :skip
          else exit
          end
        end
      end
    end
    def ocn                                                                      #and auto segment numbering increment
      required_headers_present?
      data=@data
      @o_array=[]
      node=ocn=ocn_dv=ocn_sp=ocnh=ocnh0=ocnh1=ocnh2=ocnh3=ocnh4=ocnh5=ocnh6=ocnh7=ocno=ocnp=ocnt=ocnc=ocng=ocni=ocnu=0 # h heading, o other, t table, g group, i image
      regex_exclude_ocn_and_node = /#{Rx[:meta]}|^@\S+?:\s|^4~endnotes|^#{Mx[:lv_o]}4:endnotes#{Mx[:lv_c]}|^\^~ |<:e[:_]\d+?>|^<:\#|<:- |<[:!]!4|<hr width|#{Mx[:br_endnotes]}|\A\s*\Z/mi #ocn here #&nbsp; added with Tune.code #¡
      parent=node1=node2=node3=node4=node5=node6=node7=nil
      node0='0:0;0'
      @collapsed_lv0=0
      @lev_occurences={ a: 0, b: 0, c: 0, d: 0, l1: 0, l2: 0, l3: 0, l4: 0 }
      data.each do |dob|
        h={}
        if (dob.obj !~ regex_exclude_ocn_and_node || dob.is==:code) \
        && (dob.of !=:comment \
        && dob.of !=:layout \
        && dob.of !=:meta) \
        && dob.ocn_
          #dob.ln now is determined, and set earlier, check how best to remove this -->
          if dob.is==:heading
             @ln=ln=case dob.lv
             when 'A' then 0
             when 'B' then 1
             when 'C' then 2
             when 'D' then 3
             when '1' then 4
             when '2' then 5
             when '3' then 6
             when '4' then 7
             when '5' then 8
             when '6' then 9
             end
          end
          if not dob.obj =~/~#|-#/
            ocn+=1
          end
          if @process == :complete \
          or (@fnx == @md.opt.fns \
          && @md.opt.fns =~/.sst$/)
            if dob.is==:heading \
            and (ln.to_s =~/^[0-9]/ \
            or ln.to_s =~@md.lv0 \
            or ln.to_s =~@md.lv1 \
            or ln.to_s =~@md.lv2 \
            or ln.to_s =~@md.lv3 \
            or ln.to_s =~@md.lv4 \
            or ln.to_s =~@md.lv5 \
            or ln.to_s =~@md.lv6 \
            or ln.to_s =~@md.lv7)
              if not dob.obj =~/~#|-#/
                ocnh+=1
              end
              if ln==0 \
              or ln.to_s =~@md.lv0
                @lev_occurences[:a] += 1
                if not dob.obj =~/~#|-#/
                  ocn_flag=true
                  ocnh0+=1                     #heading
                  node0="0:#{ocnh0};#{ocn}"
                else
                  #document_structure_check_info(node0,node0,:error) #fix
                  ocn_flag=false
                  node0="0:0;0"
                end
                document_structure_check_info(node0,node0)
                @collapsed_lv0=0
                collapsed_level=@collapsed_lv0
                node,ocn_sp,parent=node0,"h#{ocnh}",'ROOT'
              elsif ln==1 \
              or ln.to_s =~@md.lv1
                @lev_occurences[:b] += 1
                if not dob.obj =~/~#|-#/
                  ocn_flag=true
                  ocnh1+=1                     #heading
                  node1="1:#{ocnh1};#{ocn}"
                else
                  #document_structure_check_info(node0,node0,:error) #fix
                  ocn_flag=false
                  node1="1:0;0"
                end
                parent=if node0
                  document_structure_check_info(node1,node0)
                  @collapsed_lv1=@collapsed_lv0+1
                  node0
                else
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node0,node0,:error)
                  node0
                end
                collapsed_level=@collapsed_lv1
                node,ocn_sp,parent=node1,"h#{ocnh}",node0 #FIX
              elsif ln==2 \
              or ln.to_s =~@md.lv2
                @lev_occurences[:c] += 1
                if not dob.obj =~/~#|-#/
                  ocn_flag=true
                  ocnh2+=1
                  node2="2:#{ocnh2};#{ocn}"
                else
                  #document_structure_check_info(node0,node0,:error) #fix
                  ocn_flag=false
                  node2="2:0;0"
                end
                parent=if node1
                  document_structure_check_info(node2,node1)
                  @collapsed_lv2=@collapsed_lv1+1
                  node1
                else
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node2,node0,:error)
                  node0
                end
                collapsed_level=@collapsed_lv2
                node,ocn_sp=node2,"h#{ocnh}"
              elsif ln==3 \
              or ln.to_s =~@md.lv3
                @lev_occurences[:d] += 1
                if not dob.obj =~/~#|-#/
                  ocn_flag=true
                  ocnh3+=1
                  node3="3:#{ocnh3};#{ocn}"
                else
                  #document_structure_check_info(node0,node0,:error) #fix
                  ocn_flag=false
                  node3="3:0;0"
                end
                parent=if node2
                  document_structure_check_info(node3,node2)
                  @collapsed_lv3=@collapsed_lv2+1
                  node2
                elsif node1
                  warning_incorrect_parent_level_or_level(dob.obj)
                  puts %{parent is :A~ & this level #{dob.lv}
either parent should be level :B~
or this level should be level :B~ rather than #{dob.lv}}
                  document_structure_check_info(node3,node1,:error)
                  @collapsed_lv3=@collapsed_lv1+1
                  node1
                else
                  document_structure_check_info(node3,node0,:error)
                  warning_incorrect_parent_level_or_level(dob.obj)
                  node0
                end
                collapsed_level=@collapsed_lv3
                node,ocn_sp=node3,"h#{ocnh}"
              elsif ln==4 \
              or ln.to_s =~@md.lv4
                @lev_occurences[:l1] += 1
                if not dob.obj =~/~#|-#/
                  ocn_flag=true
                  ocnh4+=1
                  node4="4:#{ocnh4};#{ocn}"
                else
                  ocn_flag=false
                  node4="4:0;0"
                end
                parent=if node3
                  document_structure_check_info(node4,node3)
                  @collapsed_lv4=@collapsed_lv3+1
                  node3
                elsif node2
                  document_structure_check_info(node4,node2)
                  @collapsed_lv4=@collapsed_lv2+1
                  node2
                elsif node1
                  document_structure_check_info(node4,node1)
                  @collapsed_lv4=@collapsed_lv1+1
                  node1
                elsif node0
                  document_structure_check_info(node4,node0)
                  @collapsed_lv4=@collapsed_lv0+1
                  node0
                else
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node4,node0,:error)
                  node0
                end
                collapsed_level=@collapsed_lv4
                node,ocn_sp=node4,"h#{ocnh}"
              elsif ln==5 \
              or ln.to_s =~@md.lv5
                @lev_occurences[:l2] += 1
                if not dob.obj =~/~#|-#/
                  ocn_flag=true
                  ocnh5+=1
                  node5="5:#{ocnh5};#{ocn}"
                else
                  ocn_flag=false
                  node5="5:0;0"
                end
                parent=if node4
                  document_structure_check_info(node5,node4)
                  @collapsed_lv5=@collapsed_lv4+1
                  node4
                elsif node3
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node5,node3,:error)
                  @collapsed_lv5=@collapsed_lv3+1
                  node3
                elsif node2
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node5,node2,:error)
                  @collapsed_lv5=@collapsed_lv2+1
                  node2
                elsif node1
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node5,node1,:error)
                  @collapsed_lv5=@collapsed_lv1+1
                  node1
                else
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node5,node0,:error)
                  node0
                end
                collapsed_level=@collapsed_lv5
                node,ocn_sp=node5,"h#{ocnh}"
              elsif ln==6 \
              or ln.to_s =~@md.lv6
                @lev_occurences[:l3] += 1
                if not dob.obj =~/~#|-#/
                  ocn_flag=true
                  ocnh6+=1
                  node6="6:#{ocnh6};#{ocn}"
                else
                  ocn_flag=false
                  node6="6:0;0"
                end
                parent=if node5
                  document_structure_check_info(node6,node5)
                  @collapsed_lv6=@collapsed_lv5+1
                  node5
                elsif node4
                  warning_incorrect_parent_level_or_level(dob.obj)
                  puts "parent is level #4 (1~) & this level ##{dob.ln} (#{dob.lv}~)
either parent should be level #5 (2~)
or this level should be #5 (2~) rather ##{dob.ln} (#{dob.lv}~)"
                  document_structure_check_info(node6,node4,:error)
                  @collapsed_lv6=@collapsed_lv4+1
                  node4
                elsif node3
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node6,node3,:error)
                  @collapsed_lv6=@collapsed_lv3+1
                  node3
                elsif node2
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node6,node2,:error)
                  @collapsed_lv6=@collapsed_lv2+1
                  node2
                elsif node1
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node6,node1,:error)
                  @collapsed_lv6=@collapsed_lv1+1
                  node1
                else
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node6,node0,:error)
                  node0
                end
                collapsed_level=@collapsed_lv6
                node,ocn_sp=node6,"h#{ocnh}"
              elsif ln==7 \
              or ln.to_s =~@md.lv7
                @lev_occurences[:l4] += 1
                if not dob.obj =~/~#|-#/
                  ocn_flag=true
                  ocnh7+=1
                  node7="7:#{ocnh7};#{ocn}"
                else
                  ocn_flag=false
                  node7="7:0;0"
                end
                parent=if node6
                  document_structure_check_info(node7,node6)
                  @collapsed_lv7=@collapsed_lv6+1
                  node5
                elsif node5
                  warning_incorrect_parent_level_or_level(dob.obj)
                  puts "parent is level #5 (2~) & this level ##{dob.ln} (#{dob.lv}~)
either parent should be level #6 (3~)
or this level should be #6 (3~) rather ##{dob.ln} (#{dob.lv}~)"
                  document_structure_check_info(node7,node5,:error)
                  @collapsed_lv6=@collapsed_lv5+1
                  node5
                elsif node4
                  warning_incorrect_parent_level_or_level(dob.obj)
                  puts "parent is level #4 (1~) & this level ##{dob.ln} (#{dob.lv}~)
either parent should be level 6~
or this level should be #6 (3~) rather ##{dob.ln} (#{dob.lv}~)"
                  document_structure_check_info(node7,node4,:error)
                  @collapsed_lv6=@collapsed_lv4+1
                  node4
                elsif node3
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node7,node3,:error)
                  @collapsed_lv6=@collapsed_lv3+1
                  node3
                elsif node2
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node7,node2,:error)
                  @collapsed_lv6=@collapsed_lv2+1
                  node2
                elsif node1
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node7,node1,:error)
                  @collapsed_lv6=@collapsed_lv1+1
                  node1
                else
                  warning_incorrect_parent_level_or_level(dob.obj)
                  document_structure_check_info(node7,node0,:error)
                  node0
                end
                collapsed_level=@collapsed_lv7
                node,ocn_sp=node7,"h#{ocnh}"
              end
            else
              unless @lev_occurences[:l1] > 0
                STDERR.puts %{Substantive text objects must follow a level 1~ heading and there are none at this point in processing: #{@lev_occurences[:l1]}
SKIPPED processing file:
[#{@md.opt.lng}] "#{@md.fns}"}
                puts dob.obj #.gsub(/^(.{1,80})/,'"\1"')
                exit
              end
              unless @ln >= 4
                lev=case @ln
                when 0 then 'A'
                when 1 then 'B'
                when 2 then 'C'
                when 3 then 'D'
                when 4 then '1'
                when 5 then '2'
                when 6 then '3'
                when 7 then '4'
                when 8 then '5'
                when 9 then '6'
                end
                STDERR.puts %{Substantive text objects must follow a level 1~ 2~ or 3~ heading: #{lev}~
SKIPPED processing file:
[#{@md.opt.lng}] "#{@md.fns}"}
                puts dob.obj.gsub(/^(.{1,80})/,'"\1"')
                if @md.opt.act[:no_stop][:set]==:on
                  $process_document = :skip
                  break
                else exit
                end
              end
              if not dob.obj =~/~#|-#/
                ocn_flag=true
              else
                ocn_flag=false
              end
              ocno+=1
              if dob.is==:table
                ocnt+=1
                ocn_sp,parent="t#{ocnt}",node
              elsif dob.is==:code
                ocnc+=1
                ocn_sp,parent="c#{ocnc}",node
              elsif dob.is==:group \
              || dob.is==:box \
              || dob.is==:block \
              || dob.is==:alt \
              || dob.is==:verse
                ocng+=1 #group, poem
                ocn_sp,parent="g#{ocng}",node
              elsif dob.is==:image #check
                ocni+=1
                ocn_sp,parent="i#{ocni}",node
              else ocnp+=1                                 #paragraph
                ocn_sp,parent="p#{ocnp}",node
              end
            end
          end
          if dob.is==:heading
            if ocn_flag==true
              dob.ln,dob.node,dob.ocn,dob.ocn_,dob.odv,dob.osp,dob.parent,dob.lc=
                ln,  node,    ocn,    ocn_flag, ocn_dv,ocn_sp, parent,    collapsed_level
            else
              ocnu+=1
              heading_use=:ok
              if dob.obj=~/#{Mx[:pa_non_object_no_heading]}/
                dob.obj=dob.obj.gsub(/#{Mx[:pa_non_object_no_heading]}/,'')
                heading_use=:ok
              elsif dob.obj=~/#{Mx[:pa_non_object_dummy_heading]}/
                dob.obj=dob.obj.gsub(/#{Mx[:pa_non_object_dummy_heading]}/,'')
                heading_use=:dummy
              end
              dob.ln,dob.node,dob.ocn,dob.ocn_,dob.use_,   dob.odv,dob.osp,dob.parent,dob.lc=
                ln,  node,    nil,    ocn_flag,heading_use,ocn_dv, ocn_sp, parent,    collapsed_level
            end
          else
            if dob.of !=:meta \
            && dob.of !=:comment \
            && dob.of !=:layout
              if ocn_flag == true
                dob.ocn,dob.ocn_,dob.odv,dob.osp,dob.parent=
                  ocn,  ocn_flag,ocn_dv, ocn_sp, parent
              else
                ocnu+=1
                dob.obj=dob.obj.gsub(/#{Mx[:fa_o]}[~-]##{Mx[:fa_c]}/,'') if dob.obj
                ocn_dv,ocn_sp="u#{ocnu}","u#{ocnu}"
                dob.ocn,dob.ocn_,dob.odv,dob.osp,dob.parent=
                  nil,  ocn_flag,ocn_dv, ocn_sp, parent
              end
            end
          end
          h
        else dob
        end
        if dob.is==:code \
        || dob.is==:verse \
        || dob.is==:alt \
        || dob.is==:box \
        || dob.is==:group \
        || dob.is==:block
          dob.obj=dob.obj.gsub(/\n+/,"\n") #newlines taken out
        end
        @o_array << dob
      end
      if @process == :complete \
      or (@fnx == @md.opt.fns \
      && @md.opt.fns =~/.sst$/)
        unless @lev_occurences[:a] == 1
          STDERR.puts %{The number of level A~ in this document: #{@lev_occurences[:a]}
There must be one level A~ (no more and no less)
SKIPPED processing file:
[#{@md.opt.lng}] "#{@md.fns}"}
          if @md.opt.act[:no_stop][:set]==:on
            $process_document = :skip
          else exit
          end
        end
        unless @lev_occurences[:l1] > 0
          STDERR.puts %{The number of level 1~ in this document: #{@lev_occurences[:l1]}
There must be at least one level 1~ (and as many as required)
SKIPPED processing file:
[#{@md.opt.lng}] "#{@md.fns}"}
          if @md.opt.act[:no_stop][:set]==:on
            $process_document = :skip
          else exit
          end
        end
      end
      @o_array
    end
  end
  class XML
    def initialize(md,data)
      @data,@md=data,md
    end
    def dom
      @s=[ 'A', 'B', 'C', 'D', '1', '2', '3' ]
      tuned_file=structure_build
      tuned_file
    end
    def spaces
      Ax[:spaces]
    end
    def structure_build
      data=@data
      tuned_file=[]
      hs=[0,false,false,false]
      t={
        lv: @s[0],
        status: :open,
      }
      tuned_file << tags(t)
      if @md.opt.act[:verbose_plus][:set]==:on
        puts "\nXML sisu structure outline --->\n"
        puts "<#{@s[0]}>"
      end
      data.each_with_index do |o,i|
        if o.is==:heading \
        || o.is==:heading_insert
          case o.ln
          when 0
            tuned_file << tag_close(o.ln,hs)
            tuned_file << tag_open(o,@s)
            if @md.opt.act[:verbose_plus][:set]==:on
              puts_tag_close(o.ln,hs)
              puts_tag_open(o,@s)
            end
            hs=[0,true,false,false,false]
          when 1
            tuned_file << tag_close(o.ln,hs)
            tuned_file << tag_open(o,@s)
            if @md.opt.act[:verbose_plus][:set]==:on
              puts_tag_close(o.ln,hs)
              puts_tag_open(o,@s)
            end
            hs=[1,true,true,false,false]
          when 2
            tuned_file << tag_close(o.ln,hs)
            tuned_file << tag_open(o,@s)
            if @md.opt.act[:verbose_plus][:set]==:on
              puts_tag_close(o.ln,hs)
              puts_tag_open(o,@s)
            end
            hs=[2,true,true,true,false]
          when 3
            tuned_file << tag_close(o.ln,hs)
            tuned_file << tag_open(o,@s)
            if @md.opt.act[:verbose_plus][:set]==:on
              puts_tag_close(o.ln,hs)
              puts_tag_open(o,@s)
            end
            hs=[3,true,true,true,true]
          when 4
            tuned_file << tag_close(o.ln,hs)
            tuned_file << tag_open(o,@s)
            if @md.opt.act[:verbose_plus][:set]==:on
              puts_tag_close(o.ln,hs)
              puts_tag_open(o,@s)
            end
            hs[0]=4
          when 5
            tuned_file << tag_close(o.ln,hs)
            tuned_file << tag_open(o,@s)
            if @md.opt.act[:verbose_plus][:set]==:on
              puts_tag_close(o.ln,hs)
              puts_tag_open(o,@s)
            end
            hs[0]=5
          when 6
            tuned_file << tag_close(o.ln,hs)
            tuned_file << tag_open(o,@s)
            if @md.opt.act[:verbose_plus][:set]==:on
              puts_tag_close(o.ln,hs)
              puts_tag_open(o,@s)
            end
            hs[0]=6
          end
        end
        tuned_file << o
      end
      if @md.opt.act[:verbose_plus][:set]==:on
        puts_tag_close(0,hs)
      end
      tuned_file << tag_close(0,hs)
      tuned_file=tuned_file.flatten
    end
    def tags(o)
      tag=(o[:status]==:open) \
      ? %{<#{o[:lv]} id="#{o[:node]}">}
      : "</#{o[:lv]}>"
      ln=case o[:lv]
      when 'A' then 0
      when 'B' then 1
      when 'C' then 2
      when 'D' then 3
      when '1' then 4
      when '2' then 5
      when '3' then 6
      when '4' then 7
      when '5' then 8
      when '6' then 9
      end
      h={
        tag: tag,
        node: o[:node],
        lv: o[:lv],
        ln: ln,
        status: o[:status],
      }
      SiSU_AO_DocumentStructure::ObjectStructure.new.xml_dom(h) #downstream code utilise else ignore like comments
    end
    def tag_open(o,tag)
      t={ lv: tag[o.ln], node: o.node, status: :open }
      t_o=tags(t)
      t_o
    end
    def tag_close(lev,hs)
      ary=[]
      case hs[0]
      when 0
        if (lev <= 0) and hs[0]
          t={
            lv: @s[0],
            status: :close,
          }
          ary << tags(t)
        end
      when 1
        if (lev <= 1) and hs[1]
          t={
            lv: @s[1],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev==0)
          t={
            lv: @s[0],
            status: :close,
          }
          ary << tags(t)
        end
      when 2
        if (lev <= 2) and hs[2]
          t={
            lv: @s[2],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 1) and hs[1]
          t={
            lv: @s[1],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev==0)
          t={
            lv: @s[0],
            status: :close,
          }
          ary << tags(t)
        end
      when 3
        if (lev <= 3) and hs[3]
          t={
            lv: @s[3],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 2) and hs[2]
          t={
            lv: @s[2],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 1) and hs[1]
          t={
            lv: @s[1],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev==0)
          t={
            lv: @s[0],
            status: :close,
          }
          ary << tags(t)
        end
      when 4
        if (lev <= 4)
          t={
            lv: @s[4],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 3) and hs[3]
          t={
            lv: @s[3],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 2) and hs[2]
          t={
            lv: @s[2],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 1) and hs[1]
          t={
            lv: @s[1],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev==0)
          t={
            lv: @s[0],
            status: :close,
          }
          ary << tags(t)
        end
      when 5
        if (lev <= 5)
          t={
            lv: @s[5],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 4)
          t={
            lv: @s[4],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 3) and hs[3]
          t={
            lv: @s[3],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 2) and hs[2]
          t={
            lv: @s[2],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 1) and hs[1]
          t={
            lv: @s[1],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev==0)
          t={
            lv: @s[0],
            status: :close,
          }
          ary << tags(t)
        end
      when 6
        if (lev <= 6)
          t={
            lv: @s[6],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 5)
          t={
            lv: @s[5],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 4)
          t={
            lv: @s[4],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 3) and hs[3]
          t={
            lv: @s[3],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 2) and hs[2]
          t={
            lv: @s[2],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev <= 1) and hs[1]
          t={
            lv: @s[1],
            status: :close,
          }
          ary << tags(t)
        end
        if (lev==0)
          t={
            lv: @s[0],
            status: :close,
          }
          ary << tags(t)
        end
      end
      ary
    end
    def puts_tag_open(o,tag)
      puts %{#{spaces*o.ln}<#{tag[o.ln]} id="#{o.node}">}
    end
    def puts_tag_close(lev,hs)
      case hs[0]
      when 0
        #puts "#{spaces*0}</#{@s[0]}>" if (lev <= 0) and hs[0]
        puts "</#{@s[0]}>"         if (lev==0)
      when 1
        puts "#{spaces*1}</#{@s[1]}>" if (lev <= 1) and hs[1]
        puts "</#{@s[0]}>"         if (lev==0)
      when 2
        puts "#{spaces*2}</#{@s[2]}>" if (lev <= 2) and hs[2]
        puts "#{spaces*1}</#{@s[1]}>" if (lev <= 1) and hs[1]
        puts "</#{@s[0]}>"         if (lev==0)
      when 3
        puts "#{spaces*3}</#{@s[3]}>" if (lev <= 3) and hs[3]
        puts "#{spaces*2}</#{@s[2]}>" if (lev <= 2) and hs[2]
        puts "#{spaces*1}</#{@s[1]}>" if (lev <= 1) and hs[1]
        puts "</#{@s[0]}>"         if (lev==0)
      when 4
        puts "#{spaces*4}</#{@s[4]}>" if (lev <= 4)
        puts "#{spaces*3}</#{@s[3]}>" if (lev <= 3) and hs[3]
        puts "#{spaces*2}</#{@s[2]}>" if (lev <= 2) and hs[2]
        puts "#{spaces*1}</#{@s[1]}>" if (lev <= 1) and hs[1]
        puts "</#{@s[0]}>"         if (lev==0)
      when 5
        puts "#{spaces*5}</#{@s[5]}>" if (lev <= 5)
        puts "#{spaces*4}</#{@s[4]}>" if (lev <= 4)
        puts "#{spaces*3}</#{@s[3]}>" if (lev <= 3) and hs[3]
        puts "#{spaces*2}</#{@s[2]}>" if (lev <= 2) and hs[2]
        puts "#{spaces*1}</#{@s[1]}>" if (lev <= 1) and hs[1]
        puts "</#{@s[0]}>"         if (lev==0)
      when 6
        puts "#{spaces*6}</#{@s[6]}>" if (lev <= 6)
        puts "#{spaces*5}</#{@s[5]}>" if (lev <= 5)
        puts "#{spaces*4}</#{@s[4]}>" if (lev <= 4)
        puts "#{spaces*3}</#{@s[3]}>" if (lev <= 3) and hs[3]
        puts "#{spaces*2}</#{@s[2]}>" if (lev <= 2) and hs[2]
        puts "#{spaces*1}</#{@s[1]}>" if (lev <= 1) and hs[1]
        puts "</#{@s[0]}>"         if (lev==0)
      end
    end
  end
end
__END__
#+END_SRC

** ao_endnotes.rb

#+HEADER: :tangle "../lib/sisu/ao_endnotes.rb_"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_Endnotes
  class Endnotes
    def initialize(md,data,endnote_array=nil)
      @md,@data,@endnote_array=
      md, data, endnote_array
      @endnote_counter,
        @endnote_counter_asterisk,
        @endnote_counter_dag=
        1,1,1
    end
    def endnotes
      data=@data
      endnote_ref=1
      @tuned_file=data.each.map do |dob|
                                                                               # manually numbered endnotes <!e(\d)!> <!e_(\d)!> -->
        if @md.opt.selections.str =~/--no-asterisk|--no-annotate/
          dob.obj=dob.obj.
            gsub(/#{Mx[:en_b_o]}\s.+?#{Mx[:en_b_c]}/,'')
        end
        if @md.opt.selections.str =~/--no-dagger|--no-annotate/
          dob.obj=dob.obj.
            gsub(/#{Mx[:en_b_o]}[+]\s.+?#{Mx[:en_b_c]}/,'')
        end
        if (defined? dob.obj) \
        && (defined? dob.is) \
        && dob.is !=:code
          case dob.obj                                                         # auto-numbered endnotes <!e!> <!e_!> -->
          when /#{Mx[:en_a_o]}.+?#{Mx[:en_a_c]}|#{Mx[:en_b_o]}[*+]\s+.+?#{Mx[:en_b_c]}/
            dob.obj=dob.obj.
              gsub(/\s*(#{Mx[:en_a_c]}|#{Mx[:en_b_c]})/,'\1')
            word_mode=dob.obj.scan(/\S+/m)
            word_mode=endnote_call_number(word_mode)
            dob.obj=word_mode.join(' ')
            endnote_ref+=1
          when /~\^(?:\s|$)/                                              #%note inserts endnotes previously gathered from /^(<!e[:_]!>|[-~]\{{3})/ (in earlier loop)
            word_mode=dob.obj.scan(/\S+/m)
            word_mode=endnote_call_number(word_mode)
            dob.obj=word_mode.join(' ')
            endnote_ref+=1
          end
        end
        dob
      end.flatten
      @endnote_counter,
        @endnote_counter_asterisk,
        @endnote_counter_dag=
        1,1,1
      @tuned_file
    end
    def endnote_call_number(words)
      words.each do |word|
        case word
        when /#{Mx[:en_a_o]}/
          unless word =~/#{Mx[:en_a_o]}[*+]+/
            word.gsub!(/#{Mx[:en_a_o]}/,
              "#{Mx[:en_a_o]}#{@endnote_counter} ")
            @endnote_counter+=1
          end
        when /#{Mx[:en_b_o]}/
          if word =~/#{Mx[:en_b_o]}[+]/
            word.gsub!(/#{Mx[:en_b_o]}[+]/,
              "#{Mx[:en_b_o]}\+#{@endnote_counter_dag} ")
            @endnote_counter_dag+=1
          else
            word.gsub!(/#{Mx[:en_b_o]}[*]?/,
              "#{Mx[:en_b_o]}\*#{@endnote_counter_asterisk} ")
            @endnote_counter_asterisk+=1
          end
        when /~\^/
          if @endnote_array
            word.gsub!(/~\^/,
              "#{@endnote_array[@endnote_counter-1]}")
            @endnote_counter+=1
          end
        end
      end
    end
  end
end
__END__
#+END_SRC

** ao_expand_insertions.rb

#+HEADER: :tangle "../lib/sisu/ao_expand_insertions.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_Insertions
  class Insertions
    def initialize(md,data)
      @md,@data=md,data
    end
    def output_filetypes_in_cmd(cmd_shortcut,lnk=nil) #make list of file types in shortcut command (as configured), e.g. when sisu -3 is used
      act_defaults=SiSU_Env::InfoProcessingFlag.new
      cmd_list=case cmd_shortcut.inspect #check on expectation, string v array
      when /0/ then act_defaults.act_0.str
      when /1/ then act_defaults.act_1.str
      when /2/ then act_defaults.act_2.str
      when /3/ then act_defaults.act_3.str
      when /4/ then act_defaults.act_4.str
      when /5/ then act_defaults.act_5.str
      when /6/ then act_defaults.act_6.str
      when /7/ then act_defaults.act_7.str
      when /8/ then act_defaults.act_8.str
      when /9/ then act_defaults.act_9.str
      end
      file_type_names={}
      file_type_names[:gen],file_type_names[:src]=[],[]
      file_type_names[:gen] <<= if cmd_list =~ /\b--manifest\b/
        "~^ { document manifest }#{lnk[:manifest]}"
      end
      file_type_names[:gen] <<= if cmd_list =~ /\b--html\b/
        [
          " { html, segmented text }#{lnk[:html_toc]}",
          " { html, scroll, document in one }#{lnk[:html_doc]}",
        ]
      end
      file_type_names[:gen] <<= if cmd_list =~ /\b--epub\b/
        [" { epub }#{lnk[:epub]}"]
      end
      file_type_names[:gen] <<= if cmd_list =~ /\b--pdf\b/ \
      or cmd_list =~ /--pdf-landscape/
        [
          " { pdf, landscape }#{lnk[:pdf_landscape]}",
        ]
      end
      file_type_names[:gen] <<= if cmd_list =~ /\b--pdf\b/ \
      or cmd_list =~ /--pdf-portrait/
        [
          " { pdf, portrait }#{lnk[:pdf_portrait]}",
        ]
      end
      file_type_names[:gen] <<= if cmd_list =~ /\b(?:--odt|--odf)\b/
        " { odf:odt, open document text }#{lnk[:odt]}"
      end
      file_type_names[:gen] <<= if cmd_list =~ /\b--xhtml\b/
        " { xhtml scroll }#{lnk[:xhtml]}"
      end
      file_type_names[:gen] <<= if cmd_list =~ /\b--docbook\b/
        " { docbook }#{lnk[:docbook]}" #CHECK
      end
      file_type_names[:gen] <<= if cmd_list =~ /\b--xml-sax\b/
        " { xml, sax }#{lnk[:xml_sax]}"
      end
      file_type_names[:gen] <<= if cmd_list =~ /\b--xml-dom\b/
        " { xml, dom }#{lnk[:xml_dom]}"
      end
      file_type_names[:gen] <<= if cmd_list =~ /\b(?:--txt|--text|--plaintext)\b/
        " { plain text utf-8 }#{lnk[:txt]}"
      end
      #file_type_names[:gen] <<= if cmd_list =~ /g/
      #  'wiki.txt'
      #end
      file_type_names[:gen] <<= if cmd_list =~ /\b--concordance\b/
        " { concordance }#{lnk[:html_concordance]}"
      end
      file_type_names[:gen] <<= if cmd_list =~ /\b--digest\b/
        " { dcc, document content certificate (digests) }#{lnk[:digest]}"
      end
      file_type_names[:src] <<= if source and cmd_shortcut =~ /\b--source\b/
        " { markup source text }#{lnk[:source]}"
      end
      file_type_names[:src] <<= if cmd_shortcut =~ /\b--sisupod\b/
        " { markup source (zipped) pod }#{lnk[:sisupod]}"
      end
      file_type_names[:gen]=file_type_names[:gen].flatten
      file_type_names[:src]=file_type_names[:src].flatten
      file_type_names
    end
    def by_language(linked_doc,lng,src=nil)
      @linked_doc,@lng,@src=linked_doc,lng,src
      @base_path="#{@md.file.output_path.base.url}/#{lng}"
      def fnh
        {
          fn: @linked_doc,
        }
      end
      def path_and_file(fn,pth)
        @base_path + '/' + pth + '/' + fn
      end
      def manifest
        fn=@md.file.base_filename.manifest(fnh)
        path_and_file(fn,'manifest')
      end
      def html_toc
        fn=@md.file.base_filename.html_segtoc(fnh)
        @base_path + '/html/' + @linked_doc + '/' + fn
      end
      def html_doc
        fn=@md.file.base_filename.html_scroll(fnh)
        path_and_file(fn,'html')
      end
      def html_concordance
        fn=@md.file.base_filename.html_concordance
        @base_path + '/html/' + @linked_doc + '/' + fn
      end
      def epub
        fn=@md.file.base_filename.epub(fnh)
        path_and_file(fn,'epub')
      end
      def pdf_landscape
        fn=@md.file.base_filename.pdf_l_a4(fnh)
        path_and_file(fn,'pdf')
      end
      def pdf_portrait
        fn=@md.file.base_filename.pdf_p_a4(fnh)
        path_and_file(fn,'pdf')
      end
      def odt
        fn=@md.file.base_filename.odt(fnh)
        path_and_file(fn,'odt')
      end
      def xhtml
        fn=@md.file.base_filename.xhtml(fnh)
        path_and_file(fn,'xhtml')
      end
      def docbook
        fn=@md.file.base_filename.xml_docbook_book(fnh)
        path_and_file(fn,'docbook')
      end
      def xml_sax
        fn=@md.file.base_filename.xml_sax(fnh)
        path_and_file(fn,'xml_sax')
      end
      def xml_dom
        fn=@md.file.base_filename.xml_dom(fnh)
        path_and_file(fn,'xml_dom')
      end
      def txt
        fn=@md.file.base_filename.txt(fnh)
        path_and_file(fn,'txt')
      end
      def digest
        fn=@md.file.base_filename.hash_digest(fnh)
        path_and_file(fn,'digest')
      end
      def source
        @base_path + '/src/' + @src
      end
      def sisupod
        @base_path + '/src/' + @src + '.zip'
      end
      self
    end
    def by_filetype(linked_doc,lng,src=nil)
      @linked_doc,@lng,@src=linked_doc,lng,src
      @lc=SiSU_Env::FilenameLanguageCodeInsert.new(@md.opt,lng).
        language_code_insert
      @base_path="#{@md.file.output_path.base.url}"
      def fnh
        {
          fn: @linked_doc,
          lng: @lc,
        }
      end
      def path_and_file(fn,pth)
        @base_path + '/' + pth + '/' + fn
      end
      def manifest
        fn=@md.file.base_filename.manifest(fnh)
        path_and_file(fn,'manifest')
      end
      def html_toc
        fn=@md.file.base_filename.html_segtoc(fnh)
        path_and_file(fn,'html')
      end
      def html_doc
        fn=@md.file.base_filename.html_scroll(fnh)
        path_and_file(fn,'html')
      end
      def html_concordance
        fn=@md.file.base_filename.html_concordance
        path_and_file(fn,'html')
      end
      def epub
        fn=@md.file.base_filename.epub(fnh)
        path_and_file(fn,'epub')
      end
      def pdf_landscape
        fn=@md.file.base_filename.pdf_l_a4(fnh)
        path_and_file(fn,'pdf')
      end
      def pdf_portrait
        fn=@md.file.base_filename.pdf_p_a4(fnh)
        path_and_file(fn,'pdf')
      end
      def odt
        fn=@md.file.base_filename.odt(fnh)
        path_and_file(fn,'odt')
      end
      def xhtml
        fn=@md.file.base_filename.xhtml(fnh)
        path_and_file(fn,'xhtml')
      end
      def docbook
        fn=@md.file.base_filename.xml_docbook_book(fnh)
        path_and_file(fn,'docbook')
      end
      def xml_sax
        fn=@md.file.base_filename.xml_sax(fnh)
        path_and_file(fn,'xml_sax')
      end
      def xml_dom
        fn=@md.file.base_filename.xml_dom(fnh)
        path_and_file(fn,'xml_dom')
      end
      def txt
        fn=@md.file.base_filename.txt(fnh)
        path_and_file(fn,'txt')
      end
      def digest
        fn=@md.file.base_filename.hash_digest(fnh)
        path_and_file(fn,'digest')
      end
      def source
        @base_path + '/src/' + @src
      end
      def sisupod
        @base_path + '/src/' + @src + '.zip'
      end
      self
    end
    def by_filename(linked_doc,lng,src=nil)
      @linked_doc,@lng,@src=linked_doc,lng,src
      @lc=SiSU_Env::FilenameLanguageCodeInsert.new(@md.opt,lng).language_code_insert
      @base_path="#{@md.file.output_path.base.url}/#{@linked_doc}"
      def fnh
        {
          fn: @linked_doc,
          lng: @lc,
        }
      end
      def path_and_file(fn,pth=nil)
        (pth.nil?) \
        ? @base_path + '/' + fn
        : @base_path + '/' + pth + '/' + fn
      end
      def manifest
        fn=@md.file.base_filename.manifest(fnh)
        path_and_file(fn)
      end
      def html_toc
        fn=@md.file.base_filename.html_segtoc(fnh)
        path_and_file(fn)
      end
      def html_doc
        fn=@md.file.base_filename.html_scroll(fnh)
        path_and_file(fn)
      end
      def html_concordance
        fn=@md.file.base_filename.html_concordance
        path_and_file(fn)
      end
      def epub
        fn=@md.file.base_filename.epub(fnh)
        path_and_file(fn,'epub')
      end
      def pdf_landscape
        fn=@md.file.base_filename.pdf_l_a4(fnh)
        path_and_file(fn)
      end
      def pdf_portrait
        fn=@md.file.base_filename.pdf_p_a4(fnh)
        path_and_file(fn)
      end
      def odt
        fn=@md.file.base_filename.odt(fnh)
        path_and_file(fn)
      end
      def xhtml
        fn=@md.file.base_filename.xhtml(fnh)
        path_and_file(fn)
      end
      def docbook
        fn=@md.file.base_filename.xml_docbook_book(fnh)
        path_and_file(fn)
      end
      def xml_sax
        fn=@md.file.base_filename.xml_sax(fnh)
        path_and_file(fn)
      end
      def xml_dom
        fn=@md.file.base_filename.xml_dom(fnh)
        path_and_file(fn)
      end
      def txt
        fn=@md.file.base_filename.txt(fnh)
        path_and_file(fn)
      end
      def digest
        fn=@md.file.base_filename.hash_digest(fnh)
        path_and_file(fn)
      end
      def source
        @base_path + '/' + @src
      end
      def sisupod
        @base_path + '/' + @src + '.zip'
      end
      self
    end
    def expand_insertions?
      data=@data
      tuned_file,tuned_file_tmp=[],[]
      codeblock_={
        status: :false,
        type:   :na,
      }
      data.each do |para|
        codeblock_=if para =~/^code(?:\.[a-z][0-9a-z_]+)?\{/ \
        and codeblock_[:status]==:false
          {
            status: :true,
            type:   :curl,
          }
        elsif para =~/^```[ ]+code(?:\.[a-z][0-9a-z_]+)?/ \
        and codeblock_[:status]==:false
          {
            status: :true,
            type:   :tics,
          }
        elsif codeblock_[:type]==:curl \
        and para =~/^\}code/m
          {
            status: :false,
            type:   :na,
          }
        elsif codeblock_[:type]==:tics \
        and para =~/^```(?:\s|$)/m
          {
            status: :false,
            type:   :na,
          }
        else codeblock_
        end
        if para !~/^%+\s/ \
        and codeblock_[:status] != :true \
        and para =~/\{(?:~\^\s+)?(.+?)\s\[(?:\d(?:[sS]*))\]\}(?:\.\.\/\S+?\/|\S+?\.ss[tm]\b)/
          @u=SiSU_Env::InfoEnv.new.url
          m_cmd=''
          if defined? @u.remote
            if /(?<m_pre>.+?)\{(?<m_txt>.+?)\s\[(?<m_cmd>\d[sS]*)\]\}(?<m_source>(?<m_linked_doc>\S+?)\.ss[tm]\b)(?<m_note>.*)/m =~ para
              m_pre=m_pre.strip
            elsif /\{(?<m_txt>.+?)\s\[(?<m_cmd>\d[sS]*)\]\}(?<m_source>(?<m_linked_doc>\S+?)\.ss[tm]\b)(?<m_note>.*)/m =~ para
            end
            if m_linked_doc =~ /(\S+?)\/(\S+)/
              m_linked_doc,m_linked_doc_lang=$1,$2
            else
              m_linked_doc,m_linked_doc_lang=m_linked_doc,@md.opt.lng_base
            end
          else
            puts "error, does currently support relative paths (reltive paths were removed, as had problems for citation, and was not suited to all output types should possibly reconsider) #{__FILE__} #{__LINE__}"
            if /\{(?:~\^\s+)?(?<m_txt>.+?)\s\[(?<m_cmd>\d[sS]*)\]\}\.\.\/(?<m_linked_doc>\S+?)\/(?<m_note>\s+#{Mx[:en_a_o]}.+?#{Mx[:en_a_c]})?/ =~ para
            end
          end
          lnk=case @md.opt.dir_structure_by
          when :language
            {
              manifest:         by_language(m_linked_doc,m_linked_doc_lang).manifest,
              html_toc:         by_language(m_linked_doc,m_linked_doc_lang).html_toc,
              html_doc:         by_language(m_linked_doc,m_linked_doc_lang).html_doc,
              epub:             by_language(m_linked_doc,m_linked_doc_lang).epub,
              pdf_landscape:    by_language(m_linked_doc,m_linked_doc_lang).pdf_landscape,
              pdf_portrait:     by_language(m_linked_doc,m_linked_doc_lang).pdf_landscape,
              odt:              by_language(m_linked_doc,m_linked_doc_lang).odt,
              xhtml:            by_language(m_linked_doc,m_linked_doc_lang).xhtml,
              docbook:          by_language(m_linked_doc,m_linked_doc_lang).docbook,
              xml_sax:          by_language(m_linked_doc,m_linked_doc_lang).xml_sax,
              xml_dom:          by_language(m_linked_doc,m_linked_doc_lang).xml_dom,
              txt:              by_language(m_linked_doc,m_linked_doc_lang).txt,
              html_concordance: by_language(m_linked_doc,m_linked_doc_lang).html_concordance,
              digest:           by_language(m_linked_doc,m_linked_doc_lang).digest,
              sisupod:          by_language(m_linked_doc,m_linked_doc_lang,m_source).sisupod,
              source:           by_language(m_linked_doc,m_linked_doc_lang,m_source).source,
            }
          when :filetype
            {
              manifest:         by_filetype(m_linked_doc,m_linked_doc_lang).manifest,
              html_toc:         by_filetype(m_linked_doc,m_linked_doc_lang).html_toc,
              html_doc:         by_filetype(m_linked_doc,m_linked_doc_lang).html_doc,
              epub:             by_filetype(m_linked_doc,m_linked_doc_lang).epub,
              pdf_landscape:    by_filetype(m_linked_doc,m_linked_doc_lang).pdf_landscape,
              pdf_portrait:     by_filetype(m_linked_doc,m_linked_doc_lang).pdf_landscape,
              odt:              by_filetype(m_linked_doc,m_linked_doc_lang).odt,
              xhtml:            by_filetype(m_linked_doc,m_linked_doc_lang).xhtml,
              docbook:          by_filetype(m_linked_doc,m_linked_doc_lang).docbook,
              xml_sax:          by_filetype(m_linked_doc,m_linked_doc_lang).xml_sax,
              xml_dom:          by_filetype(m_linked_doc,m_linked_doc_lang).xml_dom,
              txt:              by_filetype(m_linked_doc,m_linked_doc_lang).txt,
              html_concordance: by_filetype(m_linked_doc,m_linked_doc_lang).html_concordance,
              digest:           by_filetype(m_linked_doc,m_linked_doc_lang).digest,
              sisupod:          by_filetype(m_linked_doc,m_linked_doc_lang,m_source).sisupod,
              source:           by_filetype(m_linked_doc,m_linked_doc_lang,m_source).source,
            }
          else
            {
              manifest:         by_filename(m_linked_doc,m_linked_doc_lang).manifest,
              html_toc:         by_filename(m_linked_doc,m_linked_doc_lang).html_toc,
              html_doc:         by_filename(m_linked_doc,m_linked_doc_lang).html_doc,
              epub:             by_filename(m_linked_doc,m_linked_doc_lang).epub,
              pdf_landscape:    by_filename(m_linked_doc,m_linked_doc_lang).pdf_landscape,
              pdf_portrait:     by_filename(m_linked_doc,m_linked_doc_lang).pdf_landscape,
              odt:              by_filename(m_linked_doc,m_linked_doc_lang).odt,
              xhtml:            by_filename(m_linked_doc,m_linked_doc_lang).xhtml,
              docbook:          by_filename(m_linked_doc,m_linked_doc_lang).docbook,
              xml_sax:          by_filename(m_linked_doc,m_linked_doc_lang).xml_sax,
              xml_dom:          by_filename(m_linked_doc,m_linked_doc_lang).xml_dom,
              txt:              by_filename(m_linked_doc,m_linked_doc_lang).txt,
              html_concordance: by_filename(m_linked_doc,m_linked_doc_lang).html_concordance,
              digest:           by_filename(m_linked_doc,m_linked_doc_lang).digest,
              sisupod:          by_filename(m_linked_doc,m_linked_doc_lang,m_source).sisupod,
              source:           by_filename(m_linked_doc,m_linked_doc_lang,m_source).source,
            }
          end
          linked_title="#{m_pre}{#{m_txt} }#{lnk[:manifest]}#{m_note}\n\n"
          tuned_file_tmp << linked_title
          output_filetypes=output_filetypes_in_cmd(m_cmd,lnk)
          output_filetypes[:gen].each do |desc|
            if desc
              tuned_file_tmp << if @u.remote
                "#{Mx[:nbsp]*4} #{desc} "
              else # remove ...
                "[provide document placement host location]"
              end
            end
          end
          output_filetypes[:src].each do |desc|
            if desc
              tuned_file_tmp << if @u.remote
                "#{Mx[:nbsp]*4} #{desc} "
              else
                "[provide document placement host location]"
              end
            end
          end
          tuned_file << 'group{' << tuned_file_tmp.join("\n") << '}group'
          tuned_file_tmp=[]
        else tuned_file << para
        end
      end
      tuned_file
    end
  end
end
__END__
#+END_SRC

** ao_hash_digest.rb

#+HEADER: :tangle "../lib/sisu/ao_hash_digest.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_Hash
  require_relative 'shared_markup_alt.rb'               #shared_markup_alt.rb
  class ObjectDigest
    def initialize(md,data,env=nil)
      @md,@data,@env=md,data,env
      @env ||=SiSU_Env::InfoEnv.new(@md.fns,@md)
    end
    def object_digest
    # 1. clean/stripped text without any markup, paragraph, headings etc. without endnotes
    # 2. endnotes clean/stripped text digest only (there may be several endnotes within a paragraph)
    # 3. whole object, text with markup and any endnotes, (question: with or without the endnote digests??? presumption better without, [however may be easier to check with?])
    # [digests should not include other digests]
      data=@data.compact
      @tuned_file=[]
      sha_ =@env.digest(@md.opt).type
      begin
        sha_ ? (require 'digest/sha2') : (require 'digest/md5')
      rescue LoadError
        SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:fuchsia).error(sha_ + ' NOT FOUND')
      end
      data.each do |t_o|
        unless t_o.obj.is_a?(Array)
          t_o.obj=t_o.obj.strip
        end
        if (t_o.of !=:structure \
        && t_o.of  !=:comment \
        && t_o.of  !=:layout) \
        && t_o.ocn.is_a?(Fixnum)
          case sha_
          when :sha512
            for hash_class in [ Digest::SHA512 ]
              @tuned_file << stamped(t_o,hash_class)
            end
          when :sha256
            for hash_class in [ Digest::SHA256 ]
              @tuned_file << stamped(t_o,hash_class)
            end
          when :md5
            for hash_class in [ Digest::MD5 ]
              @tuned_file << stamped(t_o,hash_class)
            end
          end
        else @tuned_file << t_o unless t_o.nil?
        end
      end
      @tuned_file=@tuned_file.flatten
      #use md5 or to create hash of each ao object including ocn, & add into to each ao object
    end
    def endnote_digest(data)
      data.each.map do |en_plus|
        case en_plus
        when /#{Mx[:en_a_o]}|#{Mx[:en_b_o]}/
          if en_plus =~/#{Mx[:en_a_o]}.+?#{Mx[:en_a_c]}|#{Mx[:en_b_o]}.+?#{Mx[:en_b_c]}/
            t_o_txt,en_open,en_txt,en_close=
              /(.*?)(#{Mx[:en_a_o]}|#{Mx[:en_b_o]})(.+?)(#{Mx[:en_a_c]}|#{Mx[:en_b_c]})/m.
              match(en_plus)[1..4]
            stripped_en=SiSU_TextRepresentation::Alter.new(en_txt).strip_clean_of_markup
            digest_en_strip=case @env.digest(@md.opt).type
            when :sha512
              Digest::SHA512.hexdigest(stripped_en)
            when :sha256
              Digest::SHA256.hexdigest(stripped_en)
            when :md5
              Digest::MD5.hexdigest(stripped_en)
            else
              Digest::SHA256.hexdigest(stripped_en)
            end
            t_o_txt +
              en_open +
              en_txt +
              Mx[:id_o] +
              digest_en_strip +
              Mx[:id_c] +
              en_close
          else STDERR.puts "Error Exception - problem encountered with:\n#{en_plus}" #arbitrary exception, tidy up
          end
        else en_plus
        end
      end.join
    end
    def stamped(t_o,hash_class) #decide what hash information is most useful, is compromise necessary?
      t_o.obj=SiSU_TextRepresentation::Alter.new(t_o).strip_clean_of_extra_spaces
      #SiSU_TextRepresentation::Alter.new(t_o).strip_clean_of_markup                      #check
      #SiSU_TextRepresentation::Alter.new(t_o).semi_revert_markup                         #check
      #SiSU_TextRepresentation::ModifiedTextPlusHashDigest.new(@md,t_o).composite.dgst    #check
      unless t_o.is==:code
        case t_o.obj
        when /#{Mx[:en_a_o]}[\d*+]+\s+.+?#{Mx[:en_a_c]}|#{Mx[:en_b_o]}[*+]\d+\s+.+?#{Mx[:en_b_c]}/m
          en_and_t_o_digest=[]
          t_o.obj=t_o.obj.
            gsub(/\s*(#{Mx[:en_a_c]}|#{Mx[:en_b_c]})/m,' \1') #watch
          t_o_plus_en=t_o.obj.
            scan(/.*?#{Mx[:en_a_o]}.+?#{Mx[:en_a_c]}|.*?#{Mx[:en_b_o]}.+?#{Mx[:en_b_c]}/m)
          t_o_tail=if t_o.obj =~/(?:.*?#{Mx[:en_a_o]}.+?#{Mx[:en_a_c]}|.*?#{Mx[:en_b_o]}.+?#{Mx[:en_b_c]})+([\s\S]+)/m
            /(?:.*?#{Mx[:en_a_o]}.+?#{Mx[:en_a_c]}|.*?#{Mx[:en_b_o]}.+?#{Mx[:en_b_c]})+.*/m.match(t_o.obj)[1]
          else ''
          end
          t_o_plus_en << t_o_tail
          en_and_t_o_digest << endnote_digest(t_o_plus_en)
          en_and_t_o_digest.join(' ')
        else #@tuned << t_o + Mx[:id_o] + digest_strip + ':' + digest_all + Mx[:id_c] unless t_o.nil?
        end
      else #@tuned << t_o + Mx[:id_o] + digest_strip + ':' + digest_all + Mx[:id_c] unless t_o.nil?
      end
      t_o #KEEP intact
    end
    def strip_clean_extra_spaces(s)                                            # ao output tuned
      s=s.dup
      s=s.gsub(/[ ]+([,.;:?](?:$|\s))/,'\1') unless s =~/#{Mx[:en_a_o]}|#{Mx[:en_b_o]}/
      s=s.gsub(/ [ ]+/,' ').
        gsub(/^ [ ]+/,'').
        gsub(/ [ ]+$/,'').
        gsub(/((?:#{Mx[:fa_bold_c]}|#{Mx[:fa_italics_c]})')[ ]+(s )/,'\1\2').
        gsub(/((?:#{Mx[:fa_bold_c]}|#{Mx[:fa_italics_c]})')[ ]+(s )/,'\1\2')
    end
  end
end
__END__
#+END_SRC

** ao_idx.rb

#+HEADER: :tangle "../lib/sisu/ao_idx.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_BookIndex
  class BookIndex
    def initialize(md,data,env=nil)
      @md,@data,@env=md,data,env
      @rgx_idx=/#{Mx[:idx_o]}(?:.+?)#{Mx[:idx_c]}\s*/
      @rgx_idx_ocn_seg=/(.+?)~(\d+)~(\S+)/
      @rgx_idx_ocn=/(.+?)~(\d+)/
      @env ||=SiSU_Env::InfoEnv.new(@md.fns)
    end
    def indexing_song
      data=@data
      data,
        sisu_markup_idx_rel,
        sisu_markup_idx_rel_html_seg,
        html_idx,xhtml_idx=
          extract_book_index(data)
      data=
        clean_and_insert_index(
          data,
          sisu_markup_idx_rel_html_seg
        )
      [
        data,
        sisu_markup_idx_rel,
        sisu_markup_idx_rel_html_seg,
        html_idx,
        xhtml_idx,
      ]
    end
    def extract_book_index(data)
      tuned_file=[]
      idx_array=[]
      data.each do |dob|
        if (dob.is ==:heading \
        || dob.is ==:heading_insert) \
        && dob.ln==4
          @seg=dob.name
        end
        if defined? dob.idx \
        and dob.idx.is_a?(Hash)
          idx_array << {
            idx: dob.idx,
            ocn: dob.ocn,
            seg: @seg
          }
        end
        tuned_file << dob if dob
      end
      if idx_array.length > 0
        the_idx=construct_book_index(idx_array)
        if @md.book_idx
          idx=index(the_idx)
          sisu_markup_idx_rel,sisu_markup_idx_rel_html_seg,html_idx,  xhtml_idx=
            idx[:sst_rel],    idx[:sst_rel_html_seg],      idx[:html],idx[:xhtml]
        else
          sisu_markup_idx_rel=
            sisu_markup_idx_rel_html_seg=
            html_idx=
            xhtml_idx=
            nil
        end
      end
      [
        tuned_file,
        sisu_markup_idx_rel,
        sisu_markup_idx_rel_html_seg,
        html_idx,
        xhtml_idx,
      ]
    end
    def construct_book_index(idx_array)
      the_idx={}
      idx_array.each do |idx|
        idx[:idx].each_pair do |term,term_info|
          location=(term_info[:plus].to_i > 0) \
          ? (%{#{idx[:ocn]}-#{idx[:ocn].to_i + term_info[:plus].to_i}})
          : idx[:ocn].to_s
          the_idx[term]={} \
            unless the_idx[term] \
            and defined? the_idx[term]
          the_idx[term]['node_0_terms']=[] \
            unless the_idx[term]['node_0_terms'] \
            and defined? the_idx[term]['node_0_terms']
          the_idx[term]['node_0_terms'] << { ocn: idx[:ocn], range: location, seg: idx[:seg] }
          if term_info[:sub].is_a?(Array) \
          and term_info[:sub].length > 0
            term_info[:sub].each do |y|
              y.each_pair do |subterm,subterm_info|
                location=(subterm_info[:plus].to_i > 0) \
                ? (%{#{idx[:ocn]}-#{idx[:ocn].to_i + subterm_info[:plus].to_i}})
                : idx[:ocn].to_s
                the_idx[term]={} \
                  unless the_idx[term] \
                  and defined? the_idx[term]
                the_idx[term]['node_0_terms']=[] \
                  unless the_idx[term]['node_0_terms']\
                  and    defined? the_idx[term]['node_0_terms']
                the_idx[term]['node_1_subterms']={} \
                  unless the_idx[term]['node_1_subterms'] \
                  and defined? the_idx[term]['node_1_subterms']
                the_idx[term]['node_1_subterms'][subterm]=[] \
                  unless the_idx[term]['node_1_subterms'][subterm] \
                  and defined? the_idx[term]['node_1_subterms'][subterm]
                the_idx[term]['node_1_subterms'][subterm] <<
                  { ocn: idx[:ocn], range: location, seg: idx[:seg] }
              end
            end
          end
        end
      end
      the_idx=the_idx.sort
      the_idx
    end
    def clean_xml(str)
      str=str.gsub(/&/,'&amp;')
      str
    end
    def index(the_idx)
      @x=1
      idx={}
      idx[:sst_rel_html_seg],idx[:sst_rel],idx[:html],idx[:xhtml]=
        [],                  [],           [],        []
      h={
        obj: Mx[:br_page]
      }
      o=SiSU_AO_DocumentStructure::ObjectLayout.new.break(h)
      idx[:sst_rel_html_seg] << o
      idx[:sst_rel] << o
      h={
        lv: '1',
        name: 'index',
        obj: "Index"
      }
      o=SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h)
      idx[:sst_rel_html_seg] << o
      idx[:sst_rel] << o
      h={
        lv: '4',
        name: 'idx',
        obj: " [Index] #{Mx[:pa_non_object_dummy_heading]}"
      }
      o=SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h)
      idx[:sst_rel_html_seg] << o
      idx[:sst_rel] << o
      alph=%W[9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z]
      idx[:html] << '<p>'
      idx[:xhtml] << '<p>'
      alph.each do |x|
        if x =~/[0-9]/
          idx[:html] << ''
          idx[:xhtml] << ''
        else
          idx[:html] <<
            %{<a href="##{x}">#{x}</a>,#{$ep[:hsp]}}
          idx[:xhtml] <<
            %{<a href="##{x.downcase}">#{x}</a>,#{$ep[:hsp]}}
        end
      end
      idx[:html] << '</p>'
      idx[:xhtml] << '</p>'
      letter=alph.shift
      idx[:html] <<
        %{\n<p class="book_index_lev1"><a name="numeral"></a></p>}
      idx[:xhtml] <<
        %{\n<p class="letter" id="numeral">0 - 9</p>}
      the_idx.each do |i|
        i.each do |x|
          if x.is_a?(String)
            f=/^(\S)/.match(x)[1]
            if letter < f
              while letter < f
                if alph.length > 0
                  letter=alph.shift
                  idx[:html] <<
                    %{\n<p class="letter"><a name="#{letter}">#{letter}</a></p><p class="book_index_lev1"><a name="#{letter.downcase}"> </a></p>}
                  idx[:xhtml] <<
                    %{\n<p class="letter" id="#{letter.downcase}">#{letter}</p>}
                else break
                end
              end
            end
            idx[:sst_rel_html_seg] <<
              %{\n\n#{Mx[:fa_bold_o]}#{x},#{Mx[:fa_bold_c]} }
            idx[:sst_rel] <<
              %{\n\n#{Mx[:fa_bold_o]}#{x},#{Mx[:fa_bold_c]} }
            aname=x.gsub(/\s+/,'_')
            idx[:html] <<
              %{\n<p class="book_index_lev1"><a name="#{aname}"><b>#{x}</b></a>, }
            c=clean_xml(x.dup)
            idx[:xhtml] <<
              %{\n<p class="book_index_lev1"><b>#{c}</b>, }
            @o=idx[:sst_rel_html_seg].index(idx[:sst_rel_html_seg].last)
            @t=idx[:sst_rel].index(idx[:sst_rel].last)
            @q=idx[:html].index(idx[:html].last)
            @r=idx[:xhtml].index(idx[:xhtml].last)
            print "\n" + x + ', ' if @md.opt.act[:verbose_plus][:set]==:on
          elsif x.is_a?(Array)
            p 'array error? -->'
            print x
          elsif x.is_a?(Hash)
            if x['node_0_terms'].is_a?(Array)
              x['node_0_terms'].each do |a|
                if a[:range]
                  idx[:sst_rel_html_seg][@o]=
                    idx[:sst_rel_html_seg][@o] +
                    %{#{Mx[:lnk_o]}#{a[:range]}#{Mx[:lnk_c]}#{Mx[:rel_o]}/#{a[:seg]}.html##{a[:ocn]}#{Mx[:rel_c]}, }
                  idx[:sst_rel][@t]=
                    idx[:sst_rel][@t] +
                    %{#{Mx[:lnk_o]}#{a[:range]}#{Mx[:lnk_c]}#{Mx[:rel_o]}#{a[:ocn]}#{Mx[:rel_c]}, }
                  idx[:html][@q]=
                    idx[:html][@q] +
                    %{<a href="#{a[:seg]}.html##{a[:ocn]}">#{a[:range]}</a>, }
                  idx[:xhtml][@q]=
                    idx[:xhtml][@q] +
                    %{<a href="#{a[:seg]}.xhtml#o#{a[:ocn]}">#{a[:range]}</a>, }
                  print a[:range] + ', ' if @md.opt.act[:verbose_plus][:set]==:on
                elsif a[:ocn]
                  idx[:sst_rel_html_seg][@o]=
                    idx[:sst_rel_html_seg][@o] +
                    %{#{Mx[:lnk_o]}#{a[:ocn]}#{Mx[:lnk_c]}#{Mx[:rel_o]}#{a[:seg]}.html##{a[:ocn]}#{Mx[:rel_c]}, }
                  idx[:sst_rel][@t]=
                    idx[:sst_rel][@t] +
                    %{#{Mx[:lnk_o]}#{a[:ocn]}#{Mx[:lnk_c]}#{Mx[:rel_o]}#{a[:ocn]}#{Mx[:rel_c]}, }
                  idx[:html][@q]=
                    idx[:html][@q] +
                    %{<a href="#{a[:seg]}.html##{a[:ocn]}">#{a[:ocn]}</a>, }
                  idx[:xhtml][@q]=
                    idx[:xhtml][@q] +
                    %{<a href="#{a[:seg]}.xhtml#o#{a[:ocn]}">#{a[:ocn]}</a>, }
                  print a[:ocn] + ', ' if @md.opt.act[:verbose_plus][:set]==:on
                else p 'error'
                end
              end
              idx[:html][@q]=idx[:html][@q] + '</p>'
              idx[:xhtml][@r]=idx[:xhtml][@r] + '</p>'
            end
            if x['node_1_subterms']
             x['node_1_subterms'].sort.each do |k,y|
                if k !~/node_0_terms/
                  idx[:sst_rel_html_seg][@o]=
                    idx[:sst_rel_html_seg][@o] +
                    %{#{k}, }
                  idx[:sst_rel][@t]=
                    idx[:sst_rel][@t] +
                    %{#{k}, }
                  idx[:html][@q]=
                    idx[:html][@q] +
                    %{\n<p class="book_index_lev2">#{k}, }
                  c=clean_xml(k.dup)
                  idx[:xhtml][@r]=
                    idx[:xhtml][@r] +
                    %{\n<p class="book_index_lev2">#{c}, }
                  print "\n\t" + k + ', ' if @md.opt.act[:verbose_plus][:set]==:on
                  y.each do |z|
                    if z[:range]
                      idx[:sst_rel_html_seg][@o]=
                        idx[:sst_rel_html_seg][@o] +
                        %{#{Mx[:lnk_o]}#{z[:range]}#{Mx[:lnk_c]}#{Mx[:rel_o]}#{z[:seg]}.html##{z[:ocn]}#{Mx[:rel_c]}, }
                      idx[:sst_rel][@t]=
                        idx[:sst_rel][@t] +
                        %{#{Mx[:lnk_o]}#{z[:range]}#{Mx[:lnk_c]}#{Mx[:rel_o]}#{z[:ocn]}#{Mx[:rel_c]}, }
                      idx[:html][@q]=
                        idx[:html][@q] +
                        %{<a href="#{z[:seg]}.html##{z[:ocn]}">#{z[:range]}</a>, }
                      idx[:xhtml][@q]=
                        idx[:xhtml][@q] +
                        %{<a href="#{z[:seg]}.xhtml#o#{z[:ocn]}">#{z[:range]}</a>, }
                      print z[:range] + ', ' if @md.opt.act[:verbose_plus][:set]==:on
                    elsif z[:ocn]
                      idx[:sst_rel_html_seg][@o]=
                        idx[:sst_rel_html_seg][@o] +
                        %{#{Mx[:lnk_o]}#{z[:ocn]}#{Mx[:lnk_c]}#{Mx[:rel_o]}#{z[:seg]}.html##{z[:ocn]}#{Mx[:rel_c]}, }
                      idx[:sst_rel][@t]=
                        idx[:sst_rel][@t] +
                        %{#{Mx[:lnk_o]}#{z[:ocn]}#{Mx[:lnk_c]}#{Mx[:rel_o]}#{z[:ocn]}#{Mx[:rel_c]}, }
                      idx[:html][@q]=
                        idx[:html][@q] +
                        %{<a href="#{z[:seg]}.html##{z[:ocn]}">#{z[:ocn]}</a>, }
                      idx[:xhtml][@q]=
                        idx[:xhtml][@q] +
                        %{<a href="#{z[:seg]}.xhtml#o#{z[:ocn]}">#{z[:ocn]}</a>, }
                      print z[:ocn] + ', ' if @md.opt.act[:verbose_plus][:set]==:on
                    else p 'error'
                    end
                  end
                  idx[:html][@q]=idx[:html][@q] + '</p>'
                  idx[:xhtml][@r]=idx[:xhtml][@r] + '</p>'
                end
              end
            end
            @x +=1
          end
        end
      end
      print "\n" if @md.opt.act[:verbose_plus][:set]==:on
      idx
    end
    def screen_print(the_idx)
      the_idx.each do |i|
        i.each do |x|
          if x.is_a?(String)
            print "\n" + x + ', '
          elsif x.is_a?(Array)
            p 'array error? -->'
            print x
          elsif x.is_a?(Hash)
            if x['node_0_terms'].is_a?(Array)
              x['node_0_terms'].each do |a|
                if a[:range]
                  print a[:range] + ', '
                elsif a[:ocn]
                  print a[:ocn] + ', '
                else p 'error'
                end
              end
            end
            if x['node_1_subterms']
              x['node_1_subterms'].sort.each do |k,y|
                if k !~/node_0_terms/
                  print "\n\t" + k + ', '
                  y.each do |z|
                    if z[:range]
                      print z[:range] + ', '
                    elsif z[:ocn]
                      print z[:ocn] + ', '
                    else p 'error'
                    end
                  end
                end
              end
            end
          end
        end
      end
    end
    def output_idx(idx)
      if @md.book_idx
        path="#{@env.path.output}/#{@md.fnb}"
        Dir.mkdir(path) unless FileTest.directory?(path)
        puts "#{path}/#{@md.fn[:book_idx_html]} #{__FILE__}::#{__LINE__}"
        html_index_file=File.new("#{path}/#{@md.fn[:book_idx_html]}",'w')
        idx[:html].each {|x| html_index_file << x }
        html_index_file.close
      end
    end
    def clean_and_insert_index(data,sisu_markup_idx)
      tuned_file=[]
      data.each do |dob|
        tuned_file << dob
        if dob.obj =~/#{Mx[:br_endnotes]}/ \
        and sisu_markup_idx
          sisu_markup_idx.each do |idx|
            tuned_file << idx
          end
        end
      end
      tuned_file
    end
    def clean_index(data)                                  #check on use of dob
      data.each.map do |para|
        para.gsub(/\n*#{@rgx_idx}/m,'')
      end
    end
  end
end
__END__
#+END_SRC

** ao_images.rb

#+HEADER: :tangle "../lib/sisu/ao_images.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_Images
  class Images
    begin
      require 'rmagick'
      include Magick
    rescue LoadError
      #SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:fuchsia).mark('rmagic NOT FOUND')
    end
    def initialize(md,data)
      @md,@data=md,data
    end
    def images
      data=@data
      @rmgk=false
      imagemagick_=true      #imagemagick_=SiSU_Env::InfoSettings.new.program?('rmagick')
      if imagemagick_
        begin
          @rmgk=SiSU_Env::Load.new('rmagick').prog
        rescue
          @rmgk=false
        end
      else
        if (@md.opt.act[:verbose_plus][:set]==:on \
        || @md.opt.act[:maintenance][:set]==:on)
          SiSU_Screen::Ansi.new(
            @md.opt.act[:color_state][:set],
            '*WARN* use of rmagick is not enabled in sisurc.yml'
          ).warn
        end
      end
      data.select do |dob|
        unless dob.is ==:table
          dob.obj=dob.obj.strip
          if dob.obj =~/#{Mx[:lnk_o]}\s*\S+\.(?:png|jpg|gif)(?:\s*|\s+.+)?#{Mx[:lnk_c]}(?:#{Mx[:url_o]}\S+?#{Mx[:url_c]}|image)/
            if dob.obj !~/#{Mx[:lnk_o]}\s*\S+\.(?:png|jpg|gif)\s+\d+x\d+/
              m=/#{Mx[:lnk_o]}\s*(\S+\.(?:png|jpg|gif))/
              if imagemagick_
                imgs=dob.obj.scan(m).flatten
                img_col=img_row=nil
                images=imgs.each do |image|
                  dir=SiSU_Env::InfoEnv.new(@md.fns)
                  path_image=[
                    dir.path.image_source_include_local,
                    dir.path.image_source_include_remote,
                    dir.path.image_source_include
                  ]
                  image_path=nil
                  path_image.each do |img_pth|
                    image_path=img_pth
                    break if FileTest.exist?("#{img_pth}/#{image}")
                  end
                  if FileTest.exist?("#{image_path}/#{image}")
                    if @rmgk
                      img=Magick::ImageList.new("#{image_path}/#{image}")
                      img_col,img_row=img.columns,img.rows
                    else
                      if (@md.opt.act[:verbose_plus][:set]==:on \
                      || @md.opt.act[:maintenance][:set]==:on)
                        SiSU_Screen::Ansi.new(
                          @md.opt.act[:color_state][:set],
                          '*WARN* rmagick not present, will attempt to use imagemagick (identify) directly'
                        ).warn
                      end
                      imgk=SiSU_Env::SystemCall.new.imagemagick
                      gmgk=SiSU_Env::SystemCall.new.graphicsmagick
                      if imgk or gmgk
                        if imgk
                          imgsys=`identify #{image_path}/#{image}`.strip                           #system call
                        elsif gmgk
                          imgsys=`gm identify #{image_path}/#{image}`.strip                        #system call
                        end
                        img_col,img_row=/(\d+)x(\d+)/m.match(imgsys)[1,2]
                        img_col,img_row=img_col.to_i,img_row.to_i
                      else
                        errmsg='imagemagick or graphicsmagick are required to process images'
                        if @md.opt.act[:no_stop][:set]==:on
                          SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:fuchsia).
                            error("#{errmsg}, proceeding (as requested) without image processing")
                          break
                        else
                          SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:fuchsia).
                            error("#{errmsg}, STOPPING")
                          exit
                        end
                      end
                    end
                    row=((img && defined? img.rows) ? img.rows : img_row)
                    col=((img && defined? img.columns) ? img.columns : img_col)
                    if img_col > img_row                                                           #landscape
                      if img_col> 640
                        img_col=640
                        img_row=((1.00*img_col/col)*row).round
                      end
                    else                                                                           #portrait
                      if img_col> 640
                        img_col=640
                        img_row=((1.00*img_col/col)*row).round
                      end
                      if img_row > 640
                        img_row=640
                        img_col=((1.00*img_row/row)*col).round
                      end
                    end
                    dob.obj=dob.obj.gsub(/(#{image})/,"#{image} #{img_col}x#{img_row}")
                  else
                    dob.obj=dob.obj.
                      gsub(/#{Mx[:lnk_o]}\s*(\S+)\.(png|jpg|gif).+?#{Mx[:lnk_c]}(#{Mx[:url_o]}\S+?#{Mx[:url_c]}|image)/,
                        '[ \1 (\2 missing) ]')
                  end
                end
              else
                images=dob.obj.scan(m) do |image|
                  SiSU_Screen::Ansi.new(
                    @md.opt.act[:color_state][:set],
                    '*WARN* where image dimensions have not been provided rmagick or imagemagick is required',image
                  ).warn unless @md.opt.act[:quiet][:set]==:on
                end
              end
            end
          end
          if dob.obj =~/#{Mx[:lnk_o]}\s*\S+\.(?:png|jpg|gif).+?#{Mx[:lnk_c]}(?:#{Mx[:url_o]}\S+?#{Mx[:url_c]}|image)/
            dob.obj=dob.obj.gsub(/(#{Mx[:lnk_o]})\s*(\S+\.(?:png|jpg|gif))\s+/i,'\1\2 ')
          end
        end
        dob unless dob.nil?
      end
    end
  end
end
__END__
imgsys=`identify #{image_path}/#{image}`.strip
#+END_SRC

** ao_metadata.rb

#+HEADER: :tangle "../lib/sisu/ao_metadata.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_Metadata
  class Metadata
    def initialize(md,metad)
      @md,@metadata=md,metad
      l=SiSU_Env::StandardiseLanguage.new(@md.opt.lng).language
      language=l[:n]
      @tr=SiSU_Translate::Source.new(md,language)
    end
    def make_para(obj,ocn)
      h={
        obj: obj,
        ocn: 0
      }
      SiSU_AO_DocumentStructure::ObjectPara.new.paragraph(h)
    end
    def make_heading(obj,ocn,name,lv,ln)
      h={
        lv: lv,
        ln: ln,
        name: name,
        obj: obj,
        ocn: 0
      }
      SiSU_AO_DocumentStructure::ObjectHeading.new.heading(h)
    end
    def metadata
    end
  end
end
__END__
#+END_SRC

** ao_misc_arrange.rb

#+HEADER: :tangle "../lib/sisu/ao_misc_arrange.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_MiscArrangeText
  class SI
    def initialize(md,data)
      @md,@data=md,data
    end
    def conditional_headings(para)
      para=para.gsub(/^(:?A~)\s*$/,'\1~ @title @author').            #conditional header
        gsub(/^((?:[1-9]|:?[A-D])~\S*)\s*$/,
          '\1~ [Note: heading marker::required title missing]~#')    #conditional header for incorporated document 2004w12
      if para =~/^@\S+?:/
        para=para.gsub(/^@(\S+?):(\s+|$)/,
            "#{Mx[:meta_o]}\\1#{Mx[:meta_c]}\\2").
          gsub(/^@(\S+?):([+-])(\s+|$)/,
            "#{Mx[:meta_o]}\\1\\2#{Mx[:meta_c]}\\3")
      end
      para
    end
    def markup_blocks(para)
      def ticks(para)
        block_open,block_close,text=nil,nil,nil
        if para =~/\A```[ ]+(?:code(?:\.[a-z][0-9a-z_]+)?|box(?:\.[a-z_]+)?|poem|alt|group|block|table).*?\n.+?\n```(?:\s+[~-][#]|\s+\~\{.+?\}\~)?\s*\Z/m
          @flag=:close
          block_open,text,block_close=
            /\A(```[ ]+(?:code(?:\.[a-z][0-9a-z_]+)?|box(?:\.[a-z_]+)?|poem|alt|group|block|table).*?)\n(.+?)\n(```([ ]+[~-][#]|\s+\~\{.+?\}\~)?)\s*\Z/m.
            match(para)[1..3]
          ((para=~/^```[ ]+table(?:~h)?\s+/) \
          and (para !~/^```[ ]+table(?:~h)?\s+c\d+/)) \
          ? para
          : (para=[]; para << block_open << text << block_close)
        elsif para =~/\A```[ ]+(?:code(?:\.[a-z][0-9a-z_]+)?|box(?:\.[a-z_]+)?|poem|alt|group|block|table).*?\n.*?\Z/m #look at, study
          @flag=:open
          block_open,text=/\A(```(?:[ ]+.+?))\n(.*?)\Z/m.match(para)[1,2]
          para=[]
          if not text.to_s.empty?
            para << block_open << text
          else
            para << block_open
          end
        elsif para =~/\A.+?\n```(?:\s+\~\{.+?\}\~)?(?:\s+[~-][#])?(\s*=\{.+?\})?\s*\Z/m \
        and @flag==:open
          @flag=:close
          text,block_close=
            /\A(.+?)\n(```(?:\s+\~\{.+?\}\~)?(?:\s+[~-][#])?(?:\s+=\{.+?\})?)\s*\Z/m.match(para)[1,2]
          para=[]
          if not text.to_s.empty?
            para << text.to_s << block_close
          else
            para << block_close
          end
        else para
        end
        para
      end
      def ticks_remove(para)
        unless @md.opt.act[:quiet][:set] ==:on
          SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:fuchsia).
           mark("ticks not recognized, ticks removed from pargraph\n#{para}")
        end
        para=para.gsub(/```[ ]+\S+[ ]*/m,'').
          gsub(/```\s*/m,'').
          strip
      end
      def ticks_quote(para)
        @flag=:quote_open
        text=para
        para=[]
        if text =~ /```[ ]+quote/m
          para << '`:quote_open`'
          text=text.gsub(/```[ ]+quote/m,'')
        end
        text=if text =~/(?:\n|\A)=\{.+?\}/m                               #exclude book index from indent markup
          txt,bkidx,tail=/(.+?)((?:\n|\A)=\{.+?\}$)(.*)/m.match(text).captures
          txt=txt.gsub(/(?:\n|\A)([^`\n]+)/m,'_1 \1')
          txt + bkidx + tail
        else text.gsub(/(?:\n|\A)([^`\n]+)/m,'_1 \1')
        end
        para << text.gsub(/```/m,'')
        if text =~/```/m
          @flag=:quote_close
          para << '`:quote_close`'
        end
        para
      end
      def curly_braces(para)
        block_open,block_close,text=nil,nil,nil
        para=if para =~/\A(?:code(?:\.[a-z][0-9a-z_]+)?|box(?:\.[a-z_]+)?|poem|alt|group|block|table)\{ .+?\n.+?\n\}(?:code|box|poem|alt|group|block|table)(?: [~-][#])?\s*\Z/m
          block_open,text,block_close=
            /\A((?:code(?:\.[a-z][0-9a-z_]+)?|box(?:\.[a-z_]+)?|poem|alt|group|block|table)\{ .+?)\n(.+?)\n(\}(?:code|box|poem|alt|group|block|table)(?: [~-][#])?)\s*\Z/m.
            match(para)[1..3]
          para=[]
          para << block_open << text << block_close
        elsif para =~/\A(?:code(?:\.[a-z][0-9a-z_]+)?|box(?:\.[a-z_]+)?|poem|alt|group|block|table)\{ .+?\n.+?\Z/m
          block_open,text=
            /\A((?:code(?:\.[a-z][0-9a-z_]+)?|box(?:\.[a-z_]+)?|poem|alt|group|block|table)\{ .+?)\n(.+?)\Z/m.
            match(para)[1,2]
          para=[]
          if not text.to_s.empty?
            para << block_open << text
          else
            para << block_open
          end
        elsif para =~/\A.+?\n\}(?:code|box|poem|alt|group|block|table)(?: [~-][#])?\s*\Z/m
          text,block_close=
            /\A(.+?)\n(\}(?:code|box|poem|alt|group|block|table)(?: [~-][#])?)\s*\Z/m.
            match(para)[1,2]
          para=[]
          if not text.to_s.empty?
            para << text.to_s << block_close
          else
            para << block_close
          end
        else para
        end
        para
      end
      para=if (para =~/\A```[ ]+quote/m \
      and @flag !=:open) \
      or @flag==:quote_open
        ticks_quote(para)
      elsif para =~/\A```[ ]+(?:code(?:\.[a-z][0-9a-z_]+)?|box(?:\.[a-z_]+)?|poem|alt|group|block|table).*?\n.*?\Z/m \
      or @flag==:open
        ticks(para)
      elsif para =~/```/m
        ticks_remove(para)
      else
        para
      end
      para=if para =~/^(?:code(?:\.[a-z][0-9a-z_]+)?|box(?:\.[a-z_]+)?|poem|alt|group|block|table)\{|^\}(?:code|box|poem|alt|group|block|table)/m
        curly_braces(para)
      else
        para
      end
    end
    def prepare_text
      data=@data
      if data[0] =~ /^#!\s*(?:\/usr\/bin\/env sisu|\/usr\/bin\/sisu)/ # remove bang from top #! (however file is stripped, so will be removed provided no content precedes it)
        data[0]=data[0].gsub(/^#!\s*\/usr\/bin\/sisu/,'').
          gsub(/^#!\s*\/usr\/bin\/env sisu/,'')
      end
      if data[0] =~ /^(SiSU\s+[\d.]*|sisu-[\d.]+)$/ # SiSU identifier
        data[0]=data[0].gsub(/^(SiSU\s*[\d.]*)$/,'% \1').
          gsub(/^(sisu-[\d.]+)$/,'% \1')
      end
      data.each.map do |para|
        para=conditional_headings(para)
        markup_blocks(para)
      end.flatten
    end
  end
end
__END__
#+END_SRC

** ao_numbering.rb

#+HEADER: :tangle "../lib/sisu/ao_numbering.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_Numbering
  class Numbering
    attr_accessor :obj,:osp,:ocn,:lv,:name,:index,:comment
    @@segments_count=0
    def initialize(md,data,fnx,process)
      @md,@data,@fnx,@process=md,data,fnx,process
      @obj=@type=@ocn=@lv=@name=@index=@comment=nil
      @chosen_seg_names=[]
    end
    def chosen_seg_names(chosen,chosen_seg_name,dob,md,type)
      @chosen_seg_names=if chosen.compact.uniq.length \
      == chosen.compact.length
        chosen
      else
        if md.opt.act[:maintenance][:set]==:on
          SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:green).
            mark(
              "duplicated auto segment name: #{type} #{chosen}\n" \
              + "#{chosen}\n" \
              + " manually name level 1 segments '1~given_name'\n" \
              + 'filename: ' + md.fns + "\n" \
              + 'heading text: "' + dob.obj + '"' + "\n" \
              + 'duplication: "' + chosen_seg_name + '" (level: ' + dob.lv + '; numbering type: ' + type.to_s + ')'
            )
        end
        chosen=chosen[0..-2]
        chosen_seg_name=auto_numbering_exceptions(chosen,md,dob)
        chosen << chosen_seg_name
      end
    end
    def number_of_segments?
      if @@segments_count==0
        @data.each do |dob|
          if dob.is ==  :heading \
          and dob.lv == '1'
            @@segments_count += 1
          end
        end
        @@segments_count
      else @@segments_count
      end
    end
    def numbering_song
      begin
        data=@data
        data=number_plaintext_para(data)
        data=auto_number_heading_ie_title(data.compact) #tr issue
        data=ocn(data.compact) #watch
        data=xml(data.compact)
        data=minor_numbering(data.compact)
        if @process==:complete
          data,tags_map,ocn_html_seg_map=name_para_seg_filename(data)
        end
        data=set_heading_top(data) unless @md.set_heading_top
        [data,tags_map,ocn_html_seg_map]
      ensure
        @@segments_count=0
      end
    end
    def set_tags(tags,tag)
      tags=if not tag.empty? \
      and tag !~/^\d+$/
        tag=tag.gsub(/[^a-z0-9._-]/,'')
        [tag,tags].flatten
      else tags
      end
    end
    def number_plaintext_para(data)
      @tuned_file=[]
      data.each do |dob|
        if (dob.of !=:block \
        && dob.of !=:comment \
        && dob.of !=:layout) \
        && dob.ocn_ #and dob.obj !~ /#{Mx[:gr_o]}Th|#{Mx[:tc_o]}#{Mx[:tc_p]}#{Mx[:tc_p]}/ #FIX
          dob.obj=dob.obj.gsub(/(.+)\n/,'\1 ') #messy, but idea is that tables should retain breaks
        end
        unless dob.obj.is_a?(Array)
          dob.obj=dob.obj.gsub(/^\s+/,'').
            gsub(/\s$/,"\n")
        end
        @tuned_file << dob
      end
      @tuned_file=@tuned_file.flatten
    end
    def number_sub_heading(dob,num,title_no)
      unless dob.obj =~/\d+\.|(?:chapter|article|section|clause)\s+\d+/i #name selection arbitrary, fix
        dob.obj=case dob.name
        when /-/          then dob.obj.gsub(/^/,"#{title_no} ")
        when /^#/         then dob.obj.gsub(/^/,"#{title_no} ")
        when /^[a-z_\.]+/ then dob.obj.gsub(/^/,"#{title_no} ")
        else
          dob.name=title_no if dob.name=~/^$/ #where title contains title number
          dob.obj.gsub(/^/,"#{title_no} ") if title_no =~/\d+/ #main, where title number is to be provided #watch changed placement
        end
        if @md.toc_lev_limit \
        and @md.toc_lev_limit < num
          dob.obj=dob.obj.gsub(/^/,'!_ ') #bold line, watch
        end
      end
      dob
    end
    def heading_tag_clean(heading_tag)
      heading_tag=heading_tag.
        gsub(/[ ]+/,'_').
        gsub(/["']/,'').
        gsub(/[\/]/,'-').
        gsub(/#{Mx[:fa_bold_o]}|#{Mx[:fa_bold_c]}/,'').
        gsub(/#{Mx[:fa_italics_o]}|#{Mx[:fa_italics_c]}/,'').
        gsub(/#{Mx[:fa_underscore_o]}|#{Mx[:fa_underscore_c]}/,'').
        gsub(/#{Mx[:fa_cite_o]}|#{Mx[:fa_cite_c]}/,'').
        gsub(/#{Mx[:fa_insert_o]}|#{Mx[:fa_insert_c]}/,'').
        gsub(/#{Mx[:fa_strike_o]}|#{Mx[:fa_strike_c]}/,'').
        gsub(/#{Mx[:fa_superscript_o]}|#{Mx[:fa_superscript_c]}/,'').
        gsub(/#{Mx[:fa_subscript_o]}|#{Mx[:fa_subscript_c]}/,'').
        gsub(/#{Mx[:fa_hilite_o]}|#{Mx[:fa_hilite_c]}/,'').
        gsub(/#{Mx[:gl_bullet]}/,'')
    end
    def auto_number_heading_ie_title(data)                                             #also does some segment naming
      if defined? @md.make.num_top \
      and @md.make.num_top \
      and @md.make.num_top !~/^$/
        input||=@md.make.num_top
      end
      num_top=(input ? input.to_i : nil)
      t_no1=t_no2=t_no3=0
      if num_top
        no1=num_top; no2=(num_top + 1); no3=(num_top + 2)
      end
      chapter_number_counter=0
      data=data.compact
      @tuned_file=data.each.map do |dob| #@md.seg_names << [additions to segment names]
        title_no=nil
        if dob.is ==:heading \
        && dob.autonum_ \
        and defined? @md.make.num_top \
        and @md.make.num_top !~/^$/
          if  dob.lv=='1' \
          and dob.obj =~/^#\s|\s#(?:\s|$)/
            chapter_number_counter +=1
            dob.obj=dob.obj.gsub(/^#\s/,"#{chapter_number_counter} ").
              gsub(/#([:,]?\s|[.]?$)/,"#{chapter_number_counter}\\1")
          end
          if dob.ln==no1
            @subnumber=1
            @subnumber=0 if dob.ln==no1
          end
          if dob.ln.to_s =~/^[0-6]/ \
          and not dob.use_ ==:dummy \
          and dob.obj !~/#{Mx[:fa_o]}(?:~#|-#)#{Mx[:fa_c]}/ # <-- fix
            if dob.ln==no1
              t_no1+=1; t_no2=0; t_no3=0
              title_no="#{t_no1}"
              if @md.seg_names.is_a?(Array) \
              and not @md.seg_names.include?(title_no)
                if dob.ln==no1
                  dob.name="#{title_no}" if not dob.name
                  dob.tags=set_tags(dob.tags,title_no)
                  tag=dob.obj.
                    gsub(/(Article|Clause|Section|Chapter)\s+/,
                      "\\1_#{title_no}").
                    downcase
                  tag=heading_tag_clean(tag)
                  dob.tags=set_tags(dob.tags,tag)
                  dob.obj=(dob.obj =~/(Article|Clause|Section)\s+/) \
                  ? (dob.obj.gsub(/(Article|Clause|Section)\s+/,"\\1 #{title_no} "))
                  : (dob.obj.gsub(/^/,"#{title_no}. ")) #fix stop later
                end
                if dob.ln !=no1 \
                and dob.obj =~/^[\d.]+\s/ #fix -> if the title starts with a numbering scheme, do not auto-number, review
                  dob.name ="#{title_no}" if not dob.name
                  dob.tags=set_tags(dob.tags,title_no)
                  dob.obj=dob.obj.gsub(/^/,"#{title_no}. ")
                end
                @md.seg_names << title_no
              end
              if dob.ln!=no1 \
              and dob.name!~/^[a-z_\.]+$/ \
              and dob.obj !~/[A-Z]\.?\s/ #bug -> tmp fix, excludes A. B. C. lettering, but not roman numerals, is arbitrary, review required # not fixed, work on
                dob.tags=set_tags(dob.tags,title_no)
                dob.obj=dob.obj.gsub(/^/i,"#{title_no}. ")
              end
            end
            if dob.ln==no1         #watch because here you change dob.name
              dob.tags=set_tags(dob.tags,"h#{title_no}")
            end
            if dob.ln==no2         #watch because here you change dob.name
              t_no2+=1; t_no3=0
              title_no="#{t_no1}.#{t_no2}"
              dob.tags=set_tags(dob.tags,"h#{title_no}")
              dob=number_sub_heading(dob,no2,title_no)
            end
            if dob.ln==no3         #watch because here you change dob.name
              t_no3+=1
              title_no="#{t_no1}.#{t_no2}.#{t_no3}"
              dob.tags=set_tags(dob.tags,"h#{title_no}")
              dob=number_sub_heading(dob,no3,title_no)
            end
          elsif dob.ln.to_s =~/^[0-6]/ \
          and dob.name =~ /^[\w-]+-/ # endnotes, watch2005# endnotes, watch2005
            dob.tags=set_tags(dob.tags,dob.name)
            dob.name.gsub(/^([a-z_\.]+)-$/,'\1')
          end
        elsif dob.is ==:heading \
        and dob.autonum_ \
        and @md.markup =~/num_extract/ #AS DANGEROUS force enable with document, note already does this type of numbering for cisg, locate and coordinate logic, is currently misplaced in code, chengwei inspired 2004w23/4
          #here lies a bug, as is nil when run from -Dv --update, FIX
          if (dob.name.nil? or dob.name.empty?) \
          and dob.ln.to_s =~/^[0-9]/ \
          and dob.obj =~ /^([\d\.]+)/ #risky (must be unique) consider output to 4~~\d instead of 4~\d
            dob.name=$1
            dob.tags=set_tags(dob.tags,dob.name)
          end
          if @md.toc_lev_limit
          end
        elsif defined? dob.name \
        and  dob.name
          dob.tags=set_tags(dob.tags,dob.name)
        end
        dob.tags=dob.tags.uniq if defined? dob.tags
        dob
      end.flatten
    end
    def ocn(data)                                                                      #and auto segment numbering increment
      @tuned_file=SiSU_AO_DocumentStructureExtract::OCN.new(@md,data,@fnx,@process).ocn
      @tuned_file
    end
    def xml(data)
      @tuned_file=SiSU_AO_DocumentStructureExtract::XML.new(@md,data).dom
      @tuned_file
    end
    def minor_numbering(data)                                                          #and auto segment numbering increment
      number_small,letter_small=0,0
      letter=%w( a b c d e f g h i j k l m n o p q r s t u v w x y z )
      @tuned_file=data.each.map do |dob|
        if dob.of ==:heading \
        || dob.of ==:heading_insert \
        || dob.of ==:para \
        || dob.of ==:block
          if dob.is ==:heading \
          and dob.ln.to_s=~/^[0-9]/                                                    #% sub-number system, (baby numbering) reset with any change of major number (more obviously should be placed in number titles, but that is conditionally executed, check and move later)
            number_small,letter_small=0,0
          elsif dob.is ==:para
            if dob.obj =~/^#[ 1]/ \
            and dob.obj !~/^#\s+(?:~#)?$/
              letter_small=0
              number_small=0 if dob.obj =~ /^#1/
              number_small+=1
              dob.obj=dob.obj.gsub(/^#[ 1]/,"#{number_small}. ")
            end
            if dob.obj =~/^_# /
              dob.obj=dob.obj.gsub(/^_# /,"#{letter[letter_small]}. ")
              dob.indent='1'
              letter_small+=1
            end
          end
        end
        dob
      end.flatten
    end
    def leading_zeros_fixed_width_number(possible_seg_name)
      if possible_seg_name.to_s =~/^([0-9]+?\.|[0-9]+)$/m       #!~/[.,:-]+/
        possible_seg_name=possible_seg_name.to_s.
          gsub(/\.$/,'')
        nl=possible_seg_name.to_s.length
        zero='0'
        zeros_fixed_width=number_of_segments?.to_s.length
        zero_width=(zeros_fixed_width - nl)
        zero_width == 0 \
        ? possible_seg_name.to_s
        : zero*zero_width +
          possible_seg_name.to_s
      end
    end
    def auto_numbering_exceptions(chosen_seg_names_,md,dob)
      number_make=case dob.lv.to_i
      when 1
        @num_exc={
          t1: @num_exc[:t1] += 1,
          t2: 0,
          t3: 0,
          t4: 0
        }
        Mx[:segname_prefix_auto_num_other] + '_' \
        + @num_exc[:t1].to_s
      when 2
        @num_exc={
          t1: @num_exc[:t1],
          t2: @num_exc[:t2] += 1,
          t3: 0,
          t4: 0
        }
        Mx[:segname_prefix_auto_num_other] + '_' \
        + @num_exc[:t1].to_s + '_' \
        + @num_exc[:t2].to_s
      when 3
        @num_exc={
          t1: @num_exc[:t1],
          t2: @num_exc[:t2],
          t3: @num_exc[:t3] += 1,
          t4: 0
        }
        Mx[:segname_prefix_auto_num_other] + '_' \
        + @num_exc[:t1].to_s + '_' \
        + @num_exc[:t2].to_s + '_' \
        + @num_exc[:t3].to_s
      when 4
        @num_exc[:t4] += 1
        @num_exc={
          t1: @num_exc[:t1],
          t2: @num_exc[:t2],
          t3: @num_exc[:t3],
          t4: @num_exc[:t4] += 1
        }
        Mx[:segname_prefix_auto_num_other] + '_' \
        + @num_exc[:t1].to_s + '_' \
        + @num_exc[:t2].to_s + '_' \
        + @num_exc[:t3].to_s + '_' \
        + @num_exc[:t4].to_s
      end
    end
    def check_that_seg_names_are_unique(chosen_seg_names_,chosen_seg_name,type,md,dob)
      begin
        chosen_seg_names_ << chosen_seg_name
        chosen_seg_names_=chosen_seg_names(chosen_seg_names_,chosen_seg_name,dob,md,type)
        if chosen_seg_names_.compact.uniq.length \
        == chosen_seg_names_.compact.length
          #check that all auto given seg names are unique
          chosen_seg_names_=chosen_seg_names(chosen_seg_names_,chosen_seg_name,dob,md,type)
          chosen_seg_name
        else
          SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:green).
            mark(
              "duplicated auto segment name: #{type} #{chosen_seg_name}\n" \
              + "#{chosen_seg_names_}\n" \
              + " manually name level 1 segments '1~given_name'\n" \
              + 'filename: ' + md.fns + "\n" \
              + 'heading text: "' + dob.obj + '"' + "\n" \
              + 'duplication: "' + chosen_seg_name + '" (level: ' + dob.lv + '; numbering type: ' + type.to_s + ')'
            )
          chosen_seg_name=auto_numbering_exceptions(chosen_seg_names_,md,dob)
          check_that_seg_names_are_unique(chosen_seg_names_,chosen_seg_name,:exception,md,dob)
        end
      rescue
      end
    end
    def auto_seg_name(possible_seg_name,heading_num_is,dob,type)
      prefix=case type
      when :auto    then Mx[:segname_prefix_auto_num_provide]
      when :extract then Mx[:segname_prefix_auto_num_extract]
      else               '_'*dob.lv.to_i #should not occur
      end
      if possible_seg_name =~/^[0-9]+?\.$/m                #!~/[.,:-]+/
        possible_seg_name=possible_seg_name.
          gsub(/\.$/,'')
      end
      @chosen_seg_name=
      if dob.lv=='4' \
      and possible_seg_name.to_s =~/^[0-9]+(?:[.,:-][0-9]){3}/m
        possible_seg_name=possible_seg_name.to_s.
          gsub(/(?:[:,-]|\W)/,'.').
          gsub(/\.$/,'')
        prefix + possible_seg_name
      elsif dob.lv=='3' \
      and possible_seg_name.to_s =~/^[0-9]+(?:[.,:-][0-9]){2}/m
        possible_seg_name=possible_seg_name.to_s.
          gsub(/(?:[:,-]|\W)/,'.').
          gsub(/\.$/,'')
        prefix + possible_seg_name
      elsif dob.lv=='2' \
      and possible_seg_name.to_s =~/^[0-9]+(?:[.,:-][0-9]){1}/m
        possible_seg_name=possible_seg_name.to_s.
          gsub(/(?:[:,-]|\W)/,'.').
          gsub(/\.$/,'')
        prefix + possible_seg_name
      elsif dob.lv=='1' \
      and possible_seg_name.to_s =~/^[0-9]+[:,-]?$/m
        if possible_seg_name.to_i <= heading_num_is.to_i
          prefix + leading_zeros_fixed_width_number(possible_seg_name)
        else
          possible_seg_name=possible_seg_name.to_s.
            gsub(/(?:[:,-]|\W)/,'.').
            gsub(/\.$/,'')
          prefix + possible_seg_name
        end
      else
        @chosen_seg_name=auto_numbering_exceptions(@chosen_seg_names,md,dob)
      end
      check_that_seg_names_are_unique(@chosen_seg_names,@chosen_seg_name,type,@md,dob)
    end
    def set_name_and_tags(dob,possible_seg_name)
      if @md.seg_names.is_a?(Array) \
      and not @md.seg_names.include?(possible_seg_name)
        dob.name=possible_seg_name
        dob.tags=set_tags(dob.tags,dob.name)
        @md.seg_names << possible_seg_name
      elsif (@md.opt.act[:verbose_plus][:set]==:on \
      or @md.opt.act[:maintenance][:set]==:on)
        puts 'warn, there may be a conflicting numbering scheme'
      end
    end
    def name_para_seg_filename(data)                                                   #segment naming, remaining
      # paragraph name/numbering rules
      # manual naming overrides, manual naming may be
      #   alpha-numeric characters mixed,
      #   numeric only (a number), if
      #     all segments have been named,
      #     the numbers used are over 1000 or
      #     it is not minded that auto-numbering uses a funny scheme for naming segments (not yet implemented)
      #       [for now a warning is printed for such documents on use of maintenance or very-verbose flag]
      # auto-naming takes the form of giving numbers to segments
      # the rules for which are as follows
      #   if the title/heading text starts with a numeric, then that is used (1 3.1 3rd etc.)
      #   otherwise the level 4 segment number from the embedded document structure info is used
      #   if there is none a sequential number is designated, preceded by an underscore
      @tuned_file,@unique_auto_name=[],[]
      tags={}
      @art_filename_auto=0
      @counter=1
      if not @md.seg_autoname_safe \
      and (@md.opt.act[:verbose_plus][:set]==:on \
      || @md.opt.act[:maintenance][:set]==:on)
        puts 'manual segment names, numbers used as names, risk warning (segmented html)'
      end
      ocn_html_seg=[]
      @num_exc={ t1: 0, t2: 0, t3: 0, t4: 0 }
      data.each do |dob|
        if dob.is==:heading \
        && dob.ln \
        and dob.ln.to_s =~/^[4-7]/
          heading_num_is=/^\d+:(\d+);\d/m.match(dob.node)[1]
          if dob.ln==4 \
          and not dob.name \
          and not @md.set_heading_seg
            @md.set_heading_seg=true
          end
          if dob.name !~/^\S+/ \
          and dob.ln.to_s =~/^[5-7]/ \
          and dob.obj =~/^\s*(?:\S+\s+)?([0-9]+(?:[.,:-][0-9])+)/m
            #heading starts with a recognised numeric
            #or word followed by a recognised numeric construct,
            #use that as name
            if dob.ln==7 \
            and dob.obj =~/^\s*(?:\S+\s+)?([0-9]+(?:[.,:-][0-9]){3})/m
              possible_seg_name=$1.
                gsub(/(?:[:,-]|\W)/,'.').
                gsub(/\.$/,'')
              possible_seg_name=
                auto_seg_name(possible_seg_name,heading_num_is,dob,:extract)
              set_name_and_tags(dob,possible_seg_name)
            elsif dob.ln==6 \
            and dob.obj =~/^\s*(?:\S+\s+)?([0-9]+(?:[.,:-][0-9]){2})/m
              possible_seg_name=$1.
                gsub(/(?:[:,-]|\W)/,'.').
                gsub(/\.$/,'')
              possible_seg_name=
                auto_seg_name(possible_seg_name,heading_num_is,dob,:extract)
              set_name_and_tags(dob,possible_seg_name)
            elsif dob.ln==5 \
            and dob.obj =~/^\s*(?:\S+\s+)?([0-9]+(?:[.,:-][0-9]){1})/m
              possible_seg_name=$1.
                gsub(/(?:[:,-]|\W)/,'.').
                gsub(/\.$/,'')
              possible_seg_name=
                auto_seg_name(possible_seg_name,heading_num_is,dob,:extract)
              set_name_and_tags(dob,possible_seg_name)
            end
          end
          if dob.ln==4
            if dob.name !~/^\S+/ \
            and dob.obj =~/^\s*(?:\S+\s+)?([0-9]+)/m
              #heading starts with a recognised numeric
              #or word followed by a recognised numeric construct,
              #use that as name
              possible_seg_name=$1
              possible_seg_name=
                auto_seg_name(possible_seg_name,heading_num_is,dob,:extract)
              set_name_and_tags(dob,possible_seg_name)
            end
            if dob.name
              #extract segment name from embedded document structure info
              if @md.seg_names.is_a?(Array) \
              and not @md.seg_names.include?(dob.name)
                dob.tags=set_tags(dob.tags,dob.name)
                @md.seg_names << dob.name
              end
            else
              #if no segment name,
              #provide a numerical one
              @art_filename_auto+=1
              possible_seg_name=
                auto_seg_name(@art_filename_auto,heading_num_is,dob,:auto)
              if @md.seg_names.is_a?(Array) \
              and not @md.seg_names.include?(possible_seg_name)
               dob.name=possible_seg_name
               dob.tags=set_tags(dob.tags,dob.name)
                @md.seg_names << possible_seg_name
              else puts 'segment name (numbering) error'
              end
            end
            if not dob.name #should not occur
              puts "e r r o r -\t#{__FILE__}::#{__LINE__}\n#{dob.inspect}"
            end
          end
        end
        if (dob.is ==:heading \
        || dob.is ==:heading_insert) \
        && dob.ln==4
          @seg=dob.name
        end
        @tuned_file << if dob.is==:heading \
        && (@md.pagenew || @md.pagebreak || @md.pageline)
          m=dob.ln.to_s
          dob_tmp=[]
          if @md.pagenew.inspect =~/#{m}/
            dob_tmp <<
              SiSU_AO_DocumentStructure::ObjectLayout.new.break(Hx[:br_page_new]) <<
              dob
          elsif @md.pagebreak.inspect =~/#{m}/
            dob_tmp <<
              SiSU_AO_DocumentStructure::ObjectLayout.new.break(Hx[:br_page]) <<
              dob
          elsif @md.pageline.inspect =~/#{m}/
            dob_tmp <<
              SiSU_AO_DocumentStructure::ObjectLayout.new.break(Hx[:br_page_line]) <<
              dob
          end
          unless dob_tmp.length > 0; dob
          else                       dob_tmp
          end
        else dob
        end
        if defined? dob.ocn \
        and dob.ocn
          @segname=((dob.is==:heading || dob.is==:heading_insert) && dob.ln==4 && (defined? dob.name)) \
          ? (dob.name)
          : @segname
          tags["#{dob.ocn}"]={ segname: @segname }
          ocn_html_seg[dob.ocn]=if (dob.is==:heading || dob.is==:heading_insert)
            if dob.ln.to_s =~/[0-3]/
              {
                seg: nil,
                level: dob.ln,
              }
            #elsif dob.ln =~/[4-6]/
            else
              {
                seg: @seg,
                level: dob.ln,
              }
            end
          else
            {
              seg: @seg,
              level: nil,
            }
          end
        end
        dob.tags=dob.tags.uniq if defined? dob.tags
        if defined? dob.tags \
        and dob.tags.length > 0
          #@segname=((dob.is=='heading'|| dob.is=='heading_insert') && dob.ln==4 && (defined? dob.name)) \
          #? (dob.name) \
          #: @segname
          dob.tags.each do |y|
            tags[y]={ ocn: dob.ocn.to_s, segname: @segname }
          end
        end
        dob
      end
      ocn_html_seg.each_with_index do |ocn,i|
        if ocn \
        and ocn[:level].to_s=~/[1-3]/
          (1..4).each do |x|
            if ocn_html_seg[i+x] \
            and ocn_html_seg[i+x][:level]==4
              ocn[:seg]=ocn_html_seg[i+x][:seg]
            end
          end
        end
      end
      if @md.seg_names.length > 0
        @md.set_heading_seg=true
      end
      tuned_file=@tuned_file.flatten
      [tuned_file,tags,ocn_html_seg]
    end
    def set_heading_top(data)                                                          #% make sure no false positives
      unless @md.set_heading_top
        if (@md.opt.act[:verbose_plus][:set]==:on \
        or @md.opt.act[:maintenance][:set]==:on)
          puts "\tdocument contains no top level heading, (will have to manufacture one)"
        end
        @tuned_file=[]
        data.each do |t_o|
          unless @md.set_heading_top
            if t_o !~/^(?:#{Rx[:meta]}|@\S+:)\s/m \
            and t_o !~/\A\s*\Z/m
              @md.set_heading_top=true
              if defined? @md.title \
              and @md.title \
              and defined? @md.title.full \
              and defined? @md.creator \
              and @md.creator
                head=@md.title.main \
                ? ([@lv='1',@obj=@md.title.main])
                : ([@lv='1',@obj='[no title provided]'])
                @tuned_file << head
              end
            end
          end
          @tuned_file << t_o
        end
        @tuned_file=@tuned_file.flatten
      end
    end
    def set_heading_seg(data)                                                          #% make sure no false positives
      unless @md.set_heading_seg
        if (@md.opt.act[:verbose_plus][:set]==:on \
        or @md.opt.act[:maintenance][:set]==:on)
          puts "\tdocument contains no segment level, (will have to manufacture one)"
        end
        @tuned_file=[]
        data.each do |dob|
          unless @md.set_heading_seg
            if defined? dob.ln and dob.ln.to_s !~/^[0-3]/m \
            and dob.obj !~/\A\s*\Z/m \
            and dob.is !=:layout
              @md.set_heading_seg=true
              head=@md.title.main \
              ? (dob.ln,dob.name,dob.obj=4,'seg',@md.title.main)
              : (dob.ln,dob.name,dob.obj=4,'seg','[segment]')
              @tuned_file << head
            end
          end
          @tuned_file << dob
        end
        @tuned_file=@tuned_file.flatten
      end
    end
    def set_header_title(data)                                                         #% make sure no false positives
      unless @md.set_header_title
        if (@md.opt.act[:verbose_plus][:set]==:on \
        or @md.opt.act[:maintenance][:set]==:on)
          puts "\t no document title provided, (will have to manufacture one)"
        end
        @tuned_file=[]
        data.each do |t_o|
          unless @md.set_header_title
            if t_o !~/^%{1,2}\s/m \
            and t_o !~/\A\s*\Z/m
              @tuned_file <<
                "#{Mx[:meta_o]}title#{Mx[:meta_c]} #{@md.heading_seg_first}"
              @md.title.main=@md.heading_seg_first
              @md.set_header_title=true
            end
          end
          @tuned_file << t_o
        end
        @tuned_file=@tuned_file.flatten
      end
    end
  end
end
__END__
#+END_SRC

** ao_persist.rb

#+HEADER: :tangle "../lib/sisu/ao_persist.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_Persist
  class Persist
    @@persistance=nil
    attr_accessor :fns, :ao_arr, :idx_arr_sst, :idx_arr_tex, :idx_arr_html, :idx_arr_xhtml, :map_arr_nametags, :map_arr_ocn_htmlseg
    def initialize(args=nil)
      @@persistance=args=(args ? args : (@@persistance || persist_init_hash_values))
      @fns=args[:fns]
      @ao_arr=args[:ao_arr]
      @idx_arr_sst=args[:idx_arr_sst]
      @idx_arr_tex=args[:idx_arr_tex]
      @idx_arr_html=args[:idx_arr_html]
      @idx_arr_xhtml=args[:idx_arr_xhtml]
      @map_arr_nametags=args[:map_arr_nametags]
      @map_arr_ocn_htmlseg=args[:map_arr_ocn_htmlseg]
    end
    def fns
      @fns
    end
    def ao_arr
      @ao_arr
    end
    def idx_arr_sst
      @idx_arr_sst
    end
    def idx_arr_tex
      @idx_arr_tex
    end
    def idx_arr_html
      @idx_arr_html
    end
    def idx_arr_xhtml
      @idx_arr_xhtml
    end
    def map_arr_nametags
      @map_arr_nametags
    end
    def map_arr_ocn_htmlseg
      @map_arr_ocn_htmlseg
    end
    def persist_init_hash_values
      {
        fns:                 nil,
        ao_arr:              [],
        idx_arr_sst:         [],
        idx_arr_tex:         [],
        idx_arr_html:        [],
        idx_arr_xhtml:       [],
        map_arr_nametags:    [],
        map_arr_ocn_htmlseg: [],
      }
    end
    def persist_init
      @@persistance=nil
      Persist.new(persist_init_hash_values)
    end
  end
  class PersistDocStructExt
    @@persist=nil
    attr_accessor :ocn, :lng, :lng_is, :code, :lngsyn, :poem, :block, :box, :group, :alt, :quote, :table, :table_to
    def initialize(args=nil)
      @@persist=args=(args ? args : (@@persist || persist_init_hash_values))
      @ocn=args[:ocn]
      @lng=args[:lng]
      @lng_is=args[:lng_is]
      @code=args[:code]
      @lngsyn=args[:lngsyn]
      @poem=args[:poem]
      @block=args[:block]
      @box=args[:box]
      @group=args[:group]
      @alt=args[:alt]
      @quote=args[:quote]
      @table=args[:table]
      @table_to=args[:table_to]
    end
    def ocn
      @ocn
    end
    def lng
      @lng
    end
    def lng_is
      @lng_is
    end
    def code
      @code
    end
    def lngsyn
      @lngsyn
    end
    def poem
      @poem
    end
    def block
      @block
    end
    def box
      @box
    end
    def group
      @group
    end
    def alt
      @alt
    end
    def quote
      @quote
    end
    def table
      @table
    end
    def table_to
      @table_to
    end
    def persist_init_hash_values
      {
        ocn:         :on,
        lng:         :off,
        lng_is:      :doc_default,
        code:        :off,
        lngsyn:      :txt,
        poem:        :off,
        block:       :off,
        box:         :off,
        group:       :off,
        alt:         :off,
        quote:       :off,
        table:       :off,
        table_to:    :off,
      }
    end
    def persist_init
      @@persist=nil
      PersistDocStructExt.new(persist_init_hash_values)
    end
  end
end
__END__
#+END_SRC

** ao_syntax.rb

#+HEADER: :tangle "../lib/sisu/ao_syntax.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_Syntax
  class Words
    def initialize(line,md,mkp)
      @line,@md,@mkp=line,md,mkp
    end
  end
  class Markup
    def initialize(md='',data='',biblio=[])
      @md,@data,@bibliography=md,data,biblio
      @data_new=[]
      url_and_stub=SiSU_Env::InfoEnv.new.url
      @output_url="#{url_and_stub.remote}"
      @env=SiSU_Env::InfoEnv.new
      emph_set=if defined? @md.emphasis_set_to \
      and not @md.emphasis_set_to.nil?
        @md.emphasis_set_to
      else @env.markup_emphasis
      end
      @emph=case emph_set
      when /bold/
        emph_italics=false
        { o: Mx[:fa_bold_o], c: Mx[:fa_bold_c] }
      when /italics/
        emph_italics=true
        { o: Mx[:fa_italics_o], c: Mx[:fa_italics_c] }
      when /underscore/
        emph_italics=false
        { o: Mx[:fa_underscore_o], c: Mx[:fa_underscore_c] }
      else p __LINE__.to_s + '::' + __FILE__
      end
      @http_m=%r{\{.+?\}https?://\S+|https?:\S+|:\S+|\.\.\/\S+|#\S+|\S+?\.png\b|[*]~\S+|^#{Mx[:meta_o]}.+|#{Mx[:gr_o]}(?:code(?:\.[a-z][0-9a-z_]+)?|box(?:\.[a-z_]+)?|block|group|alt|verse)(?:-end)?#{Mx[:gr_c]}|#{Mx[:fa_o]}:br#{Mx[:fa_c]}}
      @manmkp_ital=emph_italics \
      ? '[i/*]\\{.+?\\}[i/*]'
      : '[i/]\\{.+?\\}[i/]'
      tail_m_ital=%q{(?:\s|'s\b|[.,;:?!'")]|~\^|~\\\{\s|$)}
      tail_m_bold=%{(?:(?:#{Mx[:fa_italics_c]})?(?:\s|'s\b|[.,;:?!'")]|~\^|~\\\{\s|$))?}
      bold_line=%{^!_\s.+?(?:#{Mx[:br_line]}|\n|$)}
      #ital_line=%{^/_\s.+?(?:#{Mx[:br_line]}|\n|$)} #not implemented
      @line_scan_ital=if defined? @md.italics_match_list[:str]
        /#{@http_m}|#{bold_line}|#{@manmkp_ital}#{tail_m_ital}|#{@md.italics_match_list[:str]}#{tail_m_ital}|\S+|\n/i
      end
      @manmkp_bold=emph_italics \
      ? '^!_\s.+?(?:\n|$)|[!b]\\{.+?\\}[*!b]|[*!][a-zA-Z0-9\-_]+[!]'
      : '^!_\s.+?(?:\n|$)|[*!b]\\{.+?\\}[*!b]|[*!][a-zA-Z0-9\-_]+[*!]'
      @line_scan_bold=if defined? @md.bold_match_list[:str] \
      and @md.bold_match_list[:str]
        /#{@http_m}|#{bold_line}|(?:#{@manmkp_bold}|#{@md.bold_match_list[:str]})#{tail_m_bold}|\S+|\n/i
      end
    end
    def songsheet
      @data=@data.compact
      @data.each do |dob|
        dob=breaks(dob)
        dob=if @md.sem_tag then sem(dob) else dob end #revisit
        dob=line_actions(dob)
        dob=paragraph_set(dob)
        dob=substitutions(dob)
        dob=wordlist_italics(dob)
        dob=wordlist_bold(dob)
        dob=bodymarkup(dob)
        @data_new << dob unless dob.nil?
      end
      @data_new
    end
    def sem(dob) #revisit
      dob=SiSU_Sem::Tags.new(dob,@md).rm.all
    end
    def breaks(dob)
      if dob.is !=:meta \
      && dob.is !=:comment \
      && dob.is !=:code \
      && dob.is !=:table
        dob.obj=dob.obj.
          gsub(/^-\\\\-\s*$/,"#{Mx[:br_page]}").
          gsub(/^=\\\\=\s*$/,"#{Mx[:br_page_new]}").
          gsub(/ \\\\(?: |$)/,"#{Mx[:br_line]}").
          gsub(/(?:<:?pb>)/,"#{Mx[:br_page]}").                         # depreciated
          gsub(/(?:<:?pn>)/,"#{Mx[:br_page_new]}").                     # depreciated
          gsub(/(?:<:?br>|<br \/>)/,"#{Mx[:br_line]}").                 # depreciated
          gsub(/(?:^-\.\.-\s*$)/,"#{Mx[:br_page_line]}")
      end
      dob
    end
    def wordlist_italics(dob)
      dob=dob.dup
      if (defined? @md.italics_match_list[:str] \
      and @md.italics_match_list[:str])
        dob.obj=if dob.is !=:meta \
        && dob.is !=:heading \
        && dob.is !=:heading_insert \
        && dob.is !=:code \
        && dob.is !=:layout \
        && dob.is !=:comment
          word=dob.obj.scan(@line_scan_ital)
          word=word.flatten.compact
          line_array=[]
          word.each do |w|
            unless /#{@manmkp_ital}|#{@http_m}/.match(w)
              if defined? @md.italics_match_list[:regx] \
              and @md.italics_match_list[:regx]
                w=w.gsub(@md.italics_match_list[:regx],
                  "#{Mx[:fa_italics_o]}\\1#{Mx[:fa_italics_c]}")
              else w
              end
            end
            line_array << w
          end
          line_array.join(' ')
        else dob.obj
        end
      end
      dob
    end
    def embolden(given)
      given=given.
        gsub(/^!_\s+((?:\{|#{Mx[:lnk_o]})(?:~^ )?.+?(?:\}|#{Mx[:lnk_o]})https?:\/\/\S+.*?)([#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}])/,
          "#{Mx[:fa_bold_o]} \\1 #{Mx[:fa_bold_c]}\\2").
        gsub(/^!_\s+((?:\{|#{Mx[:lnk_o]})(?:~^ )?.+?(?:\}|#{Mx[:lnk_o]})https?:\/\/\S+.*)/,
          "#{Mx[:fa_bold_o]} \\1 #{Mx[:fa_bold_c]}").
        gsub(/(?:^!_|^#{Mx[:lv_o]}[7-9]:\S*?#{Mx[:lv_c]})\s*(.+?)([#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}])/,
          "#{Mx[:fa_bold_o]}\\1#{Mx[:fa_bold_c]}\\2").
        gsub(/(?:^!_|^#{Mx[:lv_o]}[7-9]:\S*?#{Mx[:lv_c]})\s*(.+?)\s+((?:[*]~\S+\s*)+)/,
          "#{Mx[:fa_bold_o]}\\1#{Mx[:fa_bold_c]}\\2").
        gsub(/(?:^!_|^#{Mx[:lv_o]}[7-9]:\S*?#{Mx[:lv_c]})\s*(.+?)\s*([~-]#)$/,
          "#{Mx[:fa_bold_o]}\\1#{Mx[:fa_bold_c]}\\2").
        gsub(/(?:^!_\s+|^#{Mx[:lv_o]}[7-9]:\S*?#{Mx[:lv_c]}\s*)(.*)?\s*$/,
          "#{Mx[:fa_bold_o]}\\1#{Mx[:fa_bold_c]}")
    end
    def italicise(given)
      given=given.
        gsub(/^\/_\s*(.+?)([#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}])/,
          "#{Mx[:fa_italics_o]}\\1#{Mx[:fa_italics_c]}\\2").
        gsub(/^\/_\s*(.+?)\s+((?:[*]~\S+\s*)+)/,
          "#{Mx[:fa_italics_o]}\\1#{Mx[:fa_italics_c]}\\2").
        gsub(/^\/_\s*(.+?)\s*([~-]#)$/,
          "#{Mx[:fa_italics_o]}\\1#{Mx[:fa_italics_c]}\\2").
        gsub(/^\/_\s+(.*)?\s*$/,
          "#{Mx[:fa_italics_o]}\\1#{Mx[:fa_italics_c]}")
    end
    def line_actions(dob)
      dob.obj=if (dob.is !=:heading \
      && dob.is !=:heading_insert \
      && dob.is !=:comment \
      && dob.is !=:meta) \
      and dob.obj =~ /^!_\s+/
        embolden(dob.obj)
      elsif dob.obj =~ /^\/_\s+/
        italicise(dob.obj)
      else dob.obj
      end
      dob
    end
    def paragraph_set(dob)
      dob.obj=if dob.is !=:meta \
      && dob.is !=:heading \
      && dob.is !=:heading_insert \
      && dob.is !=:code \
      && dob.is !=:comment \
      && dob.is !=:table
        dob.obj.gsub(/\n/m,' ').
          gsub(/ \s+/m,' ')
      else dob.obj
      end
      dob
    end
    def substitutions(dob)
      dob=dob.dup
      dob=if @md.flag_auto_biblio \
      and @bibliography.length > 0
        dob=if dob.is !=:meta \
        && dob.is !=:heading_insert \
        && dob.is !=:code \
        && dob.is !=:comment \
        && dob.is !=:table
          @bibliography.each do |c|
            if c[:id] and not c[:id].nil? and not c[:id].empty?
              dob.obj=dob.obj.gsub(/#{c[:id]}/mi,c[:short_name])
            end
          end
          dob
        else dob
        end
        dob
      else dob
      end
      dob=if defined? @md.substitution_match_list[:match_and_replace] \
      and @md.substitution_match_list[:match_and_replace].is_a?(Array)
        dob=if dob.is !=:meta \
        && dob.is !=:heading_insert \
        && dob.is !=:code \
        && dob.is !=:comment \
        && dob.is !=:table
          if dob.obj =~/#{@md.substitution_match_list[:matches]}/
            @md.substitution_match_list[:match_and_replace].each do |x|
              dob.obj=if x[:case_s]==:i
                dob.obj.gsub(/#{x[:match]}/mi,x[:replace])
              else
                dob.obj.gsub(/#{x[:match]}/m,x[:replace])
              end
            end
          end
          dob
        else dob
        end
        dob
      else dob
      end
    end
    def wordlist_bold(dob)
      dob=dob.dup
      if (defined? @md.bold_match_list[:str] \
      and @md.bold_match_list[:str])
        dob.obj=if dob.is !=:meta \
        && dob.is !=:heading \
        && dob.is !=:heading_insert \
        && dob.is !=:code \
        && dob.is !=:comment \
        && dob.is !=:table
          line_array=[]
          word=dob.obj.scan(@line_scan_bold)
          word=word.flatten.compact
          word.each do |w|
            unless /#{@manmkp_bold}|#{@http_m}/.match(w)
              if defined? @md.bold_match_list[:regx] \
              and @md.bold_match_list[:regx]                                             #document header: @bold: [bold word list]
                w=w.gsub(@md.bold_match_list[:regx],
                  "#{Mx[:fa_bold_o]}\\1#{Mx[:fa_bold_c]}")
              end
            else
              w=if w =~ /(?:^!_|^#{Mx[:lv_o]}[7-9]:\S*?#{Mx[:lv_c]})\s+/
                embolden(w)      #bold paragraph/emphasize #may wish to remove think about 7{ 8{ conversion not satisfactory, as information is lost!
              elsif w =~/^\/_\s+/
                italicise(w)
              else w
              end
            end
            line_array << w
          end
          line_array.join(' ')
        else dob.obj
        end
      else
        dob.obj=if dob.is==:heading \
        and dob.ln.to_s =~/[7-9]/
          embolden(dob.obj)
        else dob.obj
        end
      end
      dob
    end
    def fontface_lines(dob,leader)
      while (dob.obj =~/#{Mx[:br_nl]}/ \
      and dob.obj =~/(?:#{leader})([*!\/_#])\{(.+?)\}\1/m) \
      and $2 =~/#{Mx[:br_nl]}/
        dob=if dob.obj =~/#{Mx[:br_nl]}/ \
        and dob.obj =~/(#{leader})([*!\/_#])\{(.+?)\}\2/m
          lead,fce,txt=$1,$2,$3
          dob=if txt =~/#{Mx[:br_nl]}/
            lead_break=if dob.obj =~/^#{Mx[:br_nl]}/
              dob.obj=dob.obj.sub(/^#{Mx[:br_nl]}/,'')
              Mx[:br_nl]
            else ''
            end
            txt="#{lead_break}#{fce}\{" + txt.split(Mx[:br_nl]).join("\}#{fce}#{Mx[:br_nl]}#{fce}\{") + "\}#{fce}"
            dob.obj=dob.obj.
              sub(/(?:^|#{Mx[:gl_c]}|\s+|['"]|[#{Mx[:nbsp]}#{Mx[:fa_o_c]}#{Mx[:fa_c]}#{Mx[:lnk_o]}#{Mx[:br_nl]}#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:tc_c]}#{Mx[:tc_p]}]|[\(\[\{]|\>)([*!\/_#])\{.+?\}\1/m,
                "#{lead}#{txt}")
            dob
          else dob
          end
        end
        dob
      end
      dob
    end
    def fontface(dob)
      leader=/^|#{Mx[:gl_c]}|\s+|['"]|[#{Mx[:nbsp]}#{Mx[:fa_o_c]}#{Mx[:fa_c]}#{Mx[:lnk_o]}#{Mx[:br_nl]}#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:tc_c]}#{Mx[:tc_p]}]|[\(\[\{]|[、。「‹«¿¡]|\>/
      dob=fontface_lines(dob,leader)
      dob.obj=dob.obj.
        gsub(/(#{leader})\*\{(.+?)\}\*/m,
          "\\1#{@emph[:o]}\\2#{@emph[:c]}").                                                                                                                             #emphasis
        gsub(/(#{leader})!\{(.+?)\}!/m,
          "\\1#{Mx[:fa_bold_o]}\\2#{Mx[:fa_bold_c]}").                                                                                                                   #bold
        gsub(/(#{leader})\/\{(.+?)\}\//m,
          "\\1#{Mx[:fa_italics_o]}\\2#{Mx[:fa_italics_c]}").                                                                                                             #italics
        gsub(/(#{leader})_\{(.+?)\}_/m,
          "\\1#{Mx[:fa_underscore_o]}\\2#{Mx[:fa_underscore_c]}").                                                                                                       #underscore
        gsub(/(#{leader})#\{(.+?)\}#/m,
          "\\1#{Mx[:fa_monospace_o]}\\2#{Mx[:fa_monospace_c]}").                                                                                                         #monospace
        gsub(/(^|#{Mx[:gl_c]}|\s+|['"]|[#{Mx[:nbsp]}#{Mx[:fa_o_c]}#{Mx[:fa_c]}]|\(|\>)\"\{(.+?)\}\"/m,
          "\\1#{Mx[:fa_cite_o]}\\2#{Mx[:fa_c_o]}cite#{Mx[:fa_c]}").                                                                                                      #cite /blockquote?
        gsub(/(^|[^\\])\^\{(.+?)\}\^/m,
          "\\1#{Mx[:fa_superscript_o]}\\2#{Mx[:fa_superscript_c]}").                                                                                                     #superscript
        gsub(/(^|[^\\]),\{(.+?)\},/m,
          "\\1#{Mx[:fa_subscript_o]}\\2#{Mx[:fa_subscript_c]}").                                                                                                         #subscript
        gsub(/(^|#{Mx[:gl_c]}|\s+|['"]|#{Mx[:nbsp]}|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)\+\{(.+?)\}\+/m,
          "\\1#{Mx[:fa_insert_o]}\\2#{Mx[:fa_insert_c]}").                                                                                                               #inserted text
        gsub(/(^|#{Mx[:gl_c]}|\s+|['"]|#{Mx[:nbsp]}|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)-\{(.+?)\}-/m,
          "\\1#{Mx[:fa_strike_o]}\\2#{Mx[:fa_strike_c]}").                                                                                                               #strikethrough - deleted text
        gsub(/(^|#{Mx[:gl_c]}|\s+|['"]|#{Mx[:nbsp]}|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>|\d+)\^(\S+?)\^/,
          "\\1#{Mx[:fa_superscript_o]}\\2#{Mx[:fa_superscript_c]}")                                                                                                      #superscript single word, watch digit added
      dob
    end
    def bodymarkup(dob)
      # << http://www.jus.uio.no/sisu/sisu_markup_table/markup >>
      # See: data/sisu/sample/document_samples_sisu_markup/
      ## fontface
      # *{emphasis}*        e{emphasis}e       <strong>emphasis</strong>
      # !{bold text}!       b{bold}b           <b>bold text</b>
      # _{underline}_       u{underline}u      <u>underline</u>
      # /{italics}/         i{italics}i        <i>italics</i>
      # "{citation}"        c{citation}c       <cite>citation</cite> #blockquote?
      # ^{superscript}^                        <sup>superscript</sup>
      # ,{subscript},                          <sub>subscript</sub>
      # +{inserted text}+                      <ins>inserted text</ins>
      # -{deleted text}-                       <del>deleted text</del>
      # #{monospace text}#
      #
      # {url address}:url
      # {image.png}imageurl
      # {image.png}png
      # ~{endnote}~
      # !_                                    #bold/emphasise paragraph
      # _"                                    #blockquote paragraph
      # _1                  <:i1>            #indent paragraph 1 step
      # _2                  <:i2>            #indent paragraph 2 steps
      # _3                  <:i3>            #indent paragraph 3 steps
      # _4                  <:i4>            #indent paragraph 4 steps
      # _*                                    #bullet (list) ●
      # _1*                                   #bullet (list) indented
      # _1*                                   #bullet (list) indented
      # #                                     #numbered (list) level 1
      # _#                                    #numbered (list) level 2
      dob=dob.dup
      if dob.is !=:meta \
      && dob.is !=:comment \
      && dob.is !=:code \
      && dob.is !=:table
        line_array=[]
        word=dob.obj.scan(/\S+|\n/) #unless line =~/^(?:#{Mx[:meta_o]}|%+\s)/ #visit
        if word
          word.each do |w| # _ - / # | : ! ^ ~
            unless w =~/~\{|\}~|~\[|\]~|^\^~|~\^|\*~\S+|~#|\{t?~|\{table|https?:\/\/\S+/           # do something earlier about table!!
              w=w.gsub(/\\?~/,"#{Mx[:gl_o]}#126#{Mx[:gl_c]}")                                      #escaped special character
            end
            w=w.gsub(/^\<$/,
              "#{Mx[:gl_o]}#lt#{Mx[:gl_c]}").gsub(/^\>$/,"#{Mx[:gl_o]}#gt#{Mx[:gl_c]}")            #escaped special character
            line_array << w
          end
          dob.obj=line_array.join(' ')
          dob.obj=dob.obj.strip
        end
        dob.obj=dob.obj.
          gsub(/^([*#.-]{1,12})$/,'\1 ~#').                                                        #ocn off for these paragraph separators
          gsub(/~\{(.+?)\}~/m,Mx[:en_a_o] + '\1' + Mx[:en_a_c]).
          gsub(/~\[([^*+].+?)\]~/m,Mx[:en_b_o] + '* \1' + Mx[:en_b_c]).                            #default if markup does not specify
          gsub(/~\[(.+?)\]~/m,Mx[:en_b_o] + '\1' + Mx[:en_b_c])
        if dob.is ==:heading \
        and dob.ln ==0
          dob.obj=dob.obj.gsub(/\s*@title\b/," #{@md.title.full}")
          dob.obj=if defined? @md.creator.author \
          and @md.creator.author
            dob.obj.gsub(/\s+(?:@creator|@author)/,",#{Mx[:br_line]}#{@md.creator.author}")
          else dob.obj.gsub(/\s+(?:@creator|@author)/,'')
          end
        end
        if defined? @md.title \
        and @md.title \
        and defined? @md.title.full \
        and defined? @md.creator \
        and @md.creator
          if dob.is ==:heading
            dob.obj=dob.obj.gsub(/^\s*@title\s*$/,@md.title.full) if dob.lv =~/1/
            dob.obj=if dob.lv =~/[23]/ \
            and defined? @md.creator.author \
            and @md.creator.author
              dob.obj.
                gsub(/^\s*(?:(by\s+)?(?:@creator|@author))\s*$/,
                  "\\1#{@md.creator.author}")
            else dob.obj.gsub(/^\s*(?:(by\s+)?(?:@creator|@author))\s*$/,'\1')
            end
          end
        end
        dob.obj=dob.obj.gsub(/<(https?:\/\/\S+?)>/,'< \1 >').                     #catch problem markup
          gsub(/<:=(\S+?)>/,'{ c_\1.png 14x14 }image').
          gsub(/<!(\S+)!>/,'<:\1>').                                              #escaped special character
          gsub(/&nbsp;/,"#{Mx[:nbsp]}").                                          #escaped special character
          gsub(/\\~/,"#{Mx[:gl_o]}#126#{Mx[:gl_c]}").                             #escaped special character
          gsub(/\\\{/,"#{Mx[:gl_o]}#123#{Mx[:gl_c]}").                            #escaped special character
          gsub(/\\\}/,"#{Mx[:gl_o]}#125#{Mx[:gl_c]}").                            #escaped special character
          gsub(/\\\<</,"#{Mx[:gl_o]}#lt#{Mx[:gl_c]}#{Mx[:gl_o]}#lt#{Mx[:gl_c]}"). #escaped special character
          gsub(/\\\>>/,"#{Mx[:gl_o]}#gt#{Mx[:gl_c]}#{Mx[:gl_o]}#gt#{Mx[:gl_c]}"). #escaped special character
          gsub(/\\\</,"#{Mx[:gl_o]}#lt#{Mx[:gl_c]}").                             #escaped special character
          gsub(/\\\>/,"#{Mx[:gl_o]}#gt#{Mx[:gl_c]}").                             #escaped special character
          gsub(/\\\_/,"#{Mx[:gl_o]}#095#{Mx[:gl_c]}").                            #escaped special character
          gsub(/\\\-/,"#{Mx[:gl_o]}#045#{Mx[:gl_c]}").                            #escaped special character
          gsub(/\\\+/,"#{Mx[:gl_o]}#043#{Mx[:gl_c]}").                            #escaped special character
          gsub(/\\\//,"#{Mx[:gl_o]}#047#{Mx[:gl_c]}").                            #escaped special character
          gsub(/\\\#/,"#{Mx[:gl_o]}#035#{Mx[:gl_c]}").                            #escaped special character
          gsub(/\\\&/,"#{Mx[:gl_o]}#038#{Mx[:gl_c]}").                            #&amp; #escaped special character
          gsub(/\\\|/,"#{Mx[:gl_o]}#124#{Mx[:gl_c]}").                            #not really a sisu special character but made available as possibility
          gsub(/\\\:/,"#{Mx[:gl_o]}#058#{Mx[:gl_c]}").                            #not really a sisu special character but made available as possibility
          gsub(/\\\!/,"#{Mx[:gl_o]}#033#{Mx[:gl_c]}").                            #not really a sisu special character but made available as possibility
          gsub(/\\\^/,"#{Mx[:gl_o]}#094#{Mx[:gl_c]}").                            #not really a sisu special character but made available as possibility
          gsub(/\\\,/,"#{Mx[:gl_o]}#044#{Mx[:gl_c]}").                            #not really a sisu special character but made available as possibility
          gsub(/\\\\/,"#{Mx[:gl_o]}#092#{Mx[:gl_c]}").                            #escaped special character
          gsub(/\\\*/,"#{Mx[:gl_o]}#042#{Mx[:gl_c]}").                            #escaped special character
          gsub(/\\\!/,"#{Mx[:gl_o]}#033#{Mx[:gl_c]}")                             #escaped special character
        if dob.obj=~/(?:https?:|ftp:|\{([^{}]+?)\}(?:#|:|[.]{1,2}\/))\S+/m
          if dob.obj=~/(?:^|[#{Mx[:gl_c]}#{Mx[:nbsp]} ])\{~\^ (?:.+?)\s*\}(?:(?:https?:|ftp:|:|[.]{1,2}\/)\S+?)\s*#{Mx[:en_a_o]}(.+?)#{Mx[:en_a_c]}/m
            dob.obj=dob.obj.
              gsub(/(^|[#{Mx[:gl_c]}#{Mx[:nbsp]} ])\{~\^ ([^}]+?)\s*\}((?:https?:|ftp:|:|[.]{1,2}\/)\S+?)\s*#{Mx[:en_a_o]}(.+?)#{Mx[:en_a_c]}/m,
                "\\1#{Mx[:lnk_o]}\\2#{Mx[:lnk_c]}\\3 #{Mx[:en_a_o]}\\3 \\4#{Mx[:en_a_c]}") # watch
          end
          if dob.obj=~/(?:^|[#{Mx[:gl_c]}#{Mx[:nbsp]} ])\{~\^ (?:.+?)\s*\}(?:(?:https?:|ftp:|:|[.]{1,2}\/)\S+?)([;,.]?)(?=\s|[#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}]|$)/m
            dob.obj=dob.obj.
              gsub(/(^|[#{Mx[:gl_c]}#{Mx[:nbsp]} ])\{~\^ (.+?)\s*\}((?:https?:|ftp:|:|[.]{1,2}\/)\S+?)([;,.]?)(?=\s|[#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}]|$)/m,
                "\\1#{Mx[:lnk_o]}\\2#{Mx[:lnk_c]}\\3\\4 #{Mx[:en_a_o]}\\3#{Mx[:en_a_c]} ")
          end
          dob.obj=dob.obj.
              gsub(/(^|[^#])\{\s*([^{}]+?)\s*\}((?:https?:|:|[.]{2}\/|#)\S+?)(?=\s|[#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}#{Mx[:en_a_o]}#{Mx[:en_b_o]}]|$)/,
                "\\1#{Mx[:lnk_o]}\\2#{Mx[:lnk_c]}\\3").                                                                                                                    #linked (text or image, however text cannot include modified face, e.g. bold, ital, underline)
            gsub(/(^|[#{Mx[:gl_c]}#{Mx[:lnk_c]}#{Mx[:en_a_o]}#{Mx[:en_b_o]}(\s])((?:https?|ftp):\/\/\S+?\.[^>< ]+?)([,.;'"]?)(?=[\s#{Mx[:en_a_c]}#{Mx[:en_b_c]}#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}]|$)/m,
              %{\\1#{Mx[:url_o]}\\2#{Mx[:url_c]}\\3}).
            gsub(/#{Mx[:lnk_c]}#(\S+?[^>< ]+?)([()\[\]]*[,.;:!?'"]{0,2})(?=[\s#{Mx[:en_a_c]}#{Mx[:en_b_c]}#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}]|$)/m,
              %{#{Mx[:lnk_c]}#{Mx[:rel_o]}\\1#{Mx[:rel_c]}\\2}).
            gsub(/#{Mx[:lnk_c]}:(\S+?[^>< ]+?)([()\[\]]*[,.;:!?'"]{0,2})(?=[\s#{Mx[:en_a_c]}#{Mx[:en_b_c]}#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}]|$)/m,
              %{#{Mx[:lnk_c]}#{Mx[:rel_o]}:\\1#{Mx[:rel_c]}\\2}).
            gsub(/#{Mx[:lnk_c]}[.]{2}\/(\S+?[^>< ]+?)([()\[\]]*[,.;:!?'"]{0,2})(?=[\s#{Mx[:en_a_c]}#{Mx[:en_b_c]}#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}]|$)/m,
              %{#{Mx[:lnk_c]}#{Mx[:rel_o]}:\\1#{Mx[:rel_c]}\\2})
        end
        if dob.obj=~/_(?:https?|ftp):\S+/m           # _http://url #CHECK
          dob.obj=dob.obj.gsub(/(^|[#{Mx[:gl_c]}#{Mx[:lnk_c]}#{Mx[:en_a_o]}#{Mx[:en_b_o]}(\s])(_(?:https?|ftp):\/\/\S+?\.[^>< ]+?)([,.;'"]?)(?=[\s#{Mx[:en_a_c]}#{Mx[:en_b_c]}#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}]|$)/m,
            %{\\1#{Mx[:url_o]}\\2#{Mx[:url_c]}\\3})
        end
        dob=fontface(dob)
        dob.obj=dob.obj.
          gsub(/<[:e]\s+(.+?)!?>/,
            "#{Mx[:en_a_o]}\\1#{Mx[:en_a_c]}").                                                                                                                             #not tested
          gsub(/(^|#{Mx[:br_nl]})\s*_\*\s*/,
            "\\1#{Mx[:gl_bullet]}").                                                                                                                                        #bullets, shortcut
          gsub(/=\{(.+?)\}/,
            "#{Mx[:idx_o]}\\1#{Mx[:idx_c]}").
          gsub(/^\s*_([1-9])\*\s*/,
            "#{Mx[:pa_o]}:i\\1:\\1#{Mx[:pa_c]}#{Mx[:gl_bullet]}").                                                                                                          #bullets, shortcut
          gsub(/^\s*_([1-9])\s+/,
            "#{Mx[:pa_o]}:i\\1:\\1#{Mx[:pa_c]}").                                                                                                                           #indent
          gsub(/^\s*_([1-9])!\s+(.+?)\s*$/,
            "#{Mx[:pa_o]}:i\\1:\\1#{Mx[:pa_c]}#{Mx[:fa_bold_o]}\\2#{Mx[:fa_bold_c]} ").                                                                                     #indent bold
          gsub(/^\s*__([1-9])\s+/,
            "#{Mx[:pa_o]}:i0:\\1#{Mx[:pa_c]}").                                                                                                                             #hang
          gsub(/^\s*__([1-9])!\s+(.+?)\s*$/,
            "#{Mx[:pa_o]}:i0:\\1#{Mx[:pa_c]}#{Mx[:fa_bold_o]}\\2#{Mx[:fa_bold_c]} ").                                                                                       #hangdef
          gsub(/^\s*_([0-9])_([0-9])\s+/,
            "#{Mx[:pa_o]}:i\\1:\\2#{Mx[:pa_c]}").                                                                                                                           #hang
          gsub(/^\s*_([0-9])_([0-9])!\s+(.+?)\s*$/,
            "#{Mx[:pa_o]}:i\\1:\\2#{Mx[:pa_c]}#{Mx[:fa_bold_o]}\\3#{Mx[:fa_bold_c]} ").                                                                                     #hangdef
          gsub(/<:hi>/,"#{Mx[:fa_hilite_o]}").                                                                                                                              #'<span style="background-color: rgb(255,240,196)">'). # bright yellow rgb(255,255,0) pale yellow rgb(255,255,200)
          gsub(/<:\/hi>/,"#{Mx[:fa_hilite_c]}"). #'</span>').
          gsub(/(#{Mx[:gr_o]}verse#{Mx[:gr_c]}.+)/m,"\\1\n").
          gsub(/[ ]+($)/,'\1').
          gsub(/\{\s*(.+?)\s*\}(https?:\S+?)([;,.]?)(?=\s|[#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}#{Mx[:en_a_o]}#{Mx[:en_b_o]}]|$)/,
            "#{Mx[:lnk_o]}\\1#{Mx[:lnk_c]}#{Mx[:url_o]}\\2#{Mx[:url_c]}\\3").                                                                                               #any remaining linked text or image
          gsub(/\{\s*(.+?)\s*\}(#{Mx[:url_o]}\S+?#{Mx[:url_c]})/,
            "#{Mx[:lnk_o]}\\1#{Mx[:lnk_c]}\\2").                                                                                                                            #any remaining linked text or image
          gsub(/(^|\s)([a-zA-Z0-9._-]+\@\S+?\.[a-zA-Z0-9._-]+)/,"\\1#{Mx[:url_o]}\\2#{Mx[:url_c]}").
          gsub(/(^|[ ])\{\s*(.+?)\s*\}(\S+?)([;,.]?)(?=\s|[#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}#{Mx[:en_a_o]}#{Mx[:en_b_o]}]|$)/,
            "\\1#{Mx[:lnk_o]}\\2#{Mx[:lnk_c]}\\3\\4").                                                                                                                      #any remaining linked text or image
          gsub(/\{\s*(.+?)\s*\}#([a-zA-Z0-9][a-zA-Z0-9_-]*)([;,.]?)(?=\s|[#{Mx[:br_line]}#{Mx[:br_paragraph]}#{Mx[:br_nl]}#{Mx[:en_a_o]}#{Mx[:en_b_o]}]|$)/,
            "#{Mx[:lnk_o]}\\1#{Mx[:lnk_c]}#{Mx[:rel_o]}\\2#{Mx[:rel_c]}\\3").                                                                                               #any remaining linked text or image, check need
          gsub(/\{\s*(.+?)\s*\}(#{Mx[:rel_o]}\S+?#{Mx[:rel_c]})/,
            "#{Mx[:lnk_o]}\\1#{Mx[:lnk_c]}\\2").                                                                                                                            #any remaining linked text or image, check need
          gsub(/\{\s*(.+?)\s*\}(image)/,
            "#{Mx[:lnk_o]}\\1#{Mx[:lnk_c]}\\2")                                                                                                                             #linked image
      elsif dob.is==:table
        dob=fontface(dob)
      elsif dob.is ==:code
        dob.obj=dob.obj.
          gsub(/#{Mx[:meta_o]}(\S+?)#{Mx[:meta_c]}\s*/,'@\1: ').
          gsub(/(^|#{Mx[:gl_c]}|\s)&lt;(?:br(?: \/)?)&gt;([\s,.]|$)/,'\1<br>\2') #convert <br> <br /> back, clumsy
        if dob.number_
          codeline=[]
          ln=1
          dob.obj.split(/#{Mx[:gr_o]}codeline#{Mx[:gr_c]}|<br(?: \/)?>|\n/).each_with_index do |cl,i|
            unless i == 0
              cl=cl.gsub(Mx[:br_nl],'')
              w=3-ln.to_s.length
              cl = "#{ln}#{Mx[:nbsp]*w}#{Mx[:vline]}#{cl}#{Mx[:br_nl]}"
              ln +=1
            end
            codeline << cl
          end
          codeline= codeline.join("")
          dob.obj=codeline
        else
          dob.obj=dob.obj.gsub(/#{Mx[:gr_o]}codeline#{Mx[:gr_c]}/,"\n")
        end
        dob
      else # @\S+?:
      end
      dob
    end
    def tech                                                                       #script markup planned to be more strict for technical documents
      # *{emphasis}*        e{emphasis}e       <strong>emphasis</strong>
      # !{bold text}!       b{bold}b           <b>bold text</b>
      # _{underline}_       u{underline}u      <u>underline</u>
      # /{italics}/         i{italics}i        <i>italics</i>
      # "{citation}"        c{citation}c       <cite>citation</cite>
      # ^{superscript}^                        <sup>superscript</sup>
      # ,{subscript},                          <sub>subscript</sub>
      # +{inserted text}+                      <ins>inserted text</ins>
      # -{deleted text}-                       <del>deleted text</del>
      # #{monospace text}#
      # {url address}:url
      # {image.png}imageurl
      # {image.png}png
      # ~{endnote}~
      # +1                  <!i1!>
      # +2                  <!i2!>
      puts 'tech'
      @data.each do |line|
        line=line.
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|[\(\[]|\>)e\{(.+?)\}e/,
            "\\1#{@emph[:o]}\\2#{@emph[:c]}").                                                        #emphasis
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|[\(\[]|\>)b\{(.+?)\}b/,
            "\\1#{Mx[:fa_bold_o]}\\2#{Mx[:fa_bold_c]}").                                              #bold
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|[\(\[]|\>)u\{(.+?)\}u/,
            "\\1#{Mx[:fa_underscore_o]}\\2#{Mx[:fa_underscore_c]}").                                  #underscore
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|[\(\[]|\>)c\{(.+?)\}c/,
            "\\1#{Mx[:fa_cite_o]}\\2#{Mx[:fa_c_o]}cite#{Mx[:fa_c]}").                                 #cite
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|[\(\[]|\>)i\{(.+?)\}i/,
            "\\1#{Mx[:fa_italics_o]}\\2#{Mx[:fa_italics_c]}").                                        #italics
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|[\(\[]|\>)!\{(.+?)\}!/,
            "\\1#{Mx[:fa_bold_o]}\\2#{Mx[:fa_bold_c]}").                                              #bold
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|[\(\[]|\>)\*\{(.+?)\}\*/,
            "\\1#{@emph[:o]}\\2#{@emph[:c]}").                                                        #emphasis
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|[\(\[]|\>)_\{(.+?)\}_/,
            "\\1#{Mx[:fa_underscore_o]}\\2#{Mx[:fa_underscore_c]}").                                  #underscore
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|[\(\[]|\(|\>)\/\{(.+?)\}\//,
            "\\1#{Mx[:fa_italics_o]}\\2#{Mx[:fa_italics_c]}").                                        #italics
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)\"\{(.+?)\}\"/,
            "\\1#{Mx[:fa_cite_o]}\\2#{Mx[:fa_c_o]}cite#{Mx[:fa_c]}").
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)\^\{(.+?)\}\^/,
            "\\1#{Mx[:fa_superscript_o]}\\2#{Mx[:fa_superscript_c]}").
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)9\{(.+?)\}9/,
            "\\1#{Mx[:fa_superscript_o]}\\2#{Mx[:fa_superscript_c]}").
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>),\{(.+?)\},/,
            "\\1#{Mx[:fa_subscript_o]}\\2#{Mx[:fa_subscript_c]}").
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)6\{(.+?)\}6/,
            "\\1#{Mx[:fa_subscript_o]}\\2#{Mx[:fa_subscript_c]}").
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)\+\{(.+?)\}\+/,
            "\\1#{Mx[:fa_insert_o]}\\2#{Mx[:fa_insert_c]}").
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)v\{(.+?)\}v/,
            "\\1#{Mx[:fa_insert_o]}\\2#{Mx[:fa_insert_c]}").
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)-\{(.+?)\}-/,
            "\\1#{Mx[:fa_strike_o]}\\2#{Mx[:fa_strike_c]}").
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)x\{(.+?)\}x/,
            "\\1#{Mx[:fa_strike_o]}\\2#{Mx[:fa_strike_c]}").
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)\*(\S+?)\*/,
            "\\1#{@emph[:o]}\\2#{@emph[:c]}").                                                        #emphasise single word, watch
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)\!(\S+?)\!/,
            "\\1#{Mx[:fa_bold_o]}\\2#{Mx[:fa_bold_c]}").                                              #bold single word, watch
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)\/([\(\)a-zA-Z0-9']+?)\/([ ,.;:'"~$]|[^a-zA-Z0-9])/,
            "\\1#{Mx[:fa_italics_o]}\\2#{Mx[:fa_italics_c]}\\3").                                     #italics single word, watch
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)_(\S+?)_/,
            "\\1#{Mx[:fa_underscore_o]}\\2#{Mx[:fa_underscore_c]}").                                  #underscore single word, watch
          gsub(/(^|\s+|['"]|#{Mx[:fa_o_c]}|#{Mx[:fa_c]}|\(|\>)\^(\S+?)\^/,
            "\\1#{Mx[:fa_superscript_o]}\\2#{Mx[:fa_superscript_c]}").                                #check  #superscript single word, watch digit added
          gsub(/^\s*_\([1-9]\)\(\*\+\)\s*/,
            "#{Mx[:pa_o]}:i\\1#{Mx[:pa_c]}#{Mx[:fa_o]}\\2#{Mx[:fa_c_o]}").                            #bullets, shortcut
          gsub(/^\s*_\([1-9]\)\s+/,
            "#{Mx[:pa_o]}:i\\1#{Mx[:pa_c]}"). #watch
          gsub(/^\s*__\([1-9]\)\s+/,
            "#{Mx[:pa_o]}:h\\1#{Mx[:pa_c]}"). #watch
          #line.gsub(/^\s*__\([1-9]\)!\s+/,
          #  "#{Mx[:pa_o]}:hd\\1#{Mx[:pa_c]}"). #watch
          gsub(/#{Mx[:br_line]}\s*_[12]\s+/,
            "#{Mx[:br_line]} ")                                                                      #indent used in endnotes, not implemented, replace when ready with: line.gsub(/(?:<br>|<br \/>)\s*_([12])\s+/,'<br><:i\1> ')
      end
      @data
    end
  end
end
__END__
#+END_SRC

** ao_endnotes.rb

#+HEADER: :tangle "../lib/sisu/ao_endnotes.rb"
#+BEGIN_SRC ruby
#<<sisu_document_header>>
module SiSU_AO_Endnotes
  class Endnotes
    def initialize(md,data,endnote_array=nil)
      @md,@data,@endnote_array=
      md, data, endnote_array
      @endnote_counter,
        @endnote_counter_asterisk,
        @endnote_counter_dag=
        1,1,1
    end
    def endnotes
      data=@data
      endnote_ref=1
      @tuned_file=data.each.map do |dob|
                                                                               # manually numbered endnotes <!e(\d)!> <!e_(\d)!> -->
        if @md.opt.selections.str =~/--no-asterisk|--no-annotate/
          dob.obj=dob.obj.
            gsub(/#{Mx[:en_b_o]}\s.+?#{Mx[:en_b_c]}/,'')
        end
        if @md.opt.selections.str =~/--no-dagger|--no-annotate/
          dob.obj=dob.obj.
            gsub(/#{Mx[:en_b_o]}[+]\s.+?#{Mx[:en_b_c]}/,'')
        end
        if (defined? dob.obj) \
        && (defined? dob.is) \
        && dob.is !=:code
          case dob.obj                                                         # auto-numbered endnotes <!e!> <!e_!> -->
          when /#{Mx[:en_a_o]}.+?#{Mx[:en_a_c]}|#{Mx[:en_b_o]}[*+]\s+.+?#{Mx[:en_b_c]}/
            dob.obj=dob.obj.
              gsub(/\s*(#{Mx[:en_a_c]}|#{Mx[:en_b_c]})/,'\1')
            word_mode=dob.obj.scan(/\S+/m)
            word_mode=endnote_call_number(word_mode)
            dob.obj=word_mode.join(' ')
            endnote_ref+=1
          when /~\^(?:\s|$)/                                              #%note inserts endnotes previously gathered from /^(<!e[:_]!>|[-~]\{{3})/ (in earlier loop)
            word_mode=dob.obj.scan(/\S+/m)
            word_mode=endnote_call_number(word_mode)
            dob.obj=word_mode.join(' ')
            endnote_ref+=1
          end
        end
        dob
      end.flatten
      @endnote_counter,
        @endnote_counter_asterisk,
        @endnote_counter_dag=
        1,1,1
      @tuned_file
    end
    def endnote_call_number(words)
      words.each do |word|
        case word
        when /#{Mx[:en_a_o]}/
          unless word =~/#{Mx[:en_a_o]}[*+]+/
            word.gsub!(/#{Mx[:en_a_o]}/,
              "#{Mx[:en_a_o]}#{@endnote_counter} ")
            @endnote_counter+=1
          end
        when /#{Mx[:en_b_o]}/
          if word =~/#{Mx[:en_b_o]}[+]/
            word.gsub!(/#{Mx[:en_b_o]}[+]/,
              "#{Mx[:en_b_o]}\+#{@endnote_counter_dag} ")
            @endnote_counter_dag+=1
          else
            word.gsub!(/#{Mx[:en_b_o]}[*]?/,
              "#{Mx[:en_b_o]}\*#{@endnote_counter_asterisk} ")
            @endnote_counter_asterisk+=1
          end
        when /~\^/
          if @endnote_array
            word.gsub!(/~\^/,
              "#{@endnote_array[@endnote_counter-1]}")
            @endnote_counter+=1
          end
        end
      end
    end
  end
end
__END__
#+END_SRC

* document header

#+NAME: sisu_document_header
#+BEGIN_SRC text
encoding: utf-8
- Name: SiSU

  - Description: documents, structuring, processing, publishing, search
    abstraction

  - Author: Ralph Amissah
    <ralph.amissah@gmail.com>

  - Copyright: (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
    2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019,
    2020, 2021, Ralph Amissah,
    All Rights Reserved.

  - License: GPL 3 or later:

    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 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 <http://www.gnu.org/licenses/>.

    If you have Internet connection, the latest version of the GPL should be
    available at these locations:
    <http://www.fsf.org/licensing/licenses/gpl.html>
    <http://www.gnu.org/licenses/gpl.html>

    <http://www.sisudoc.org/sisu/en/manifest/gpl.fsf.html>

  - SiSU uses:
    - Standard SiSU markup syntax,
    - Standard SiSU meta-markup syntax, and the
    - Standard SiSU object citation numbering and system

  - Homepages:
    <http://www.sisudoc.org>

  - Git
    <https://git.sisudoc.org/projects/>
    <https://git.sisudoc.org/projects/?p=software/sisu.git;a=summary>
    <https://git.sisudoc.org/projects/?p=markup/sisu-markup-samples.git;a=summary>
#+END_SRC