# encoding: utf-8
=begin

 * Name: SiSU

 * Description: a framework for document structuring, publishing and search

 * Author: Ralph Amissah

 * Copyright: (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
   2007, 2008, 2009, 2010, 2011, 2012, 2013 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

 * Hompages:
   <http://www.jus.uio.no/sisu>
   <http://www.sisudoc.org>

 * Download:
   <http://www.sisudoc.org/sisu/en/SiSU/download.html>

 * Git
   <http://sources.sisudoc.org/gitweb/?p=code/sisu.git;a=summary>
   <http://sources.sisudoc.org/?p=code/sisu.git;a=blob;f=lib/sisu/v5/texpdf.rb;hb=HEAD>

 * Ralph Amissah
   <ralph@amissah.com>
   <ralph.amissah@gmail.com>

 ** Description: LaTeX generation

=end
module SiSU_TeX
  require 'pstore'
  require_relative 'defaults'                           # defaults.rb
    include SiSU_Viz
  require_relative 'particulars'                        # particulars.rb
    include SiSU_Particulars
  require_relative 'texpdf_format'                      # texpdf_format.rb
    include SiSU_TeX_Pdf
  require_relative 'shared_metadata'                    # shared_metadata.rb
  require_relative 'prog_text_translation'              # prog_text_translation.rb
  @tex_file=@@tex_footnote_array=@@tex_col_w=[]
  @@tabular="{tabular}"
  @@column_instruct=@@squigle_close=@@tex_line_mode=@@tex_word_mode=@@line_mode=''
  @@tex_debug_counter=@@table_pagebreak_counter=@@tex_footnote_call_counter=@@tex_table_flag=@@tex_counter=@@tex_column=@@tex_columns=@@tex_columns=@@counting=0
  @@tex_pattern_margin_number=/\\\\begin\\\{tiny\\\}\\\\hspace\\\{0mm\\\}\\\\end\\\{tiny\\\}\\\{\\\\marginpar.+?\s+/
  @@n=@@tableheader=@@rights=nil
  @@date ||=SiSU_Env::InfoDate.new
  class Source
    require 'pstore'
    require_relative 'sysenv'                           # sysenv.rb
      include SiSU_Env
    include SiSU_Viz
    require_relative 'ao'                               # ao.rb
      include SiSU_AO
    include SiSU_TeX
    def initialize(opt)
      @opt=opt
      @particulars=SiSU_Particulars::CombinedSingleton.instance.get_all(opt)
      @md=@particulars.md
      @env=SiSU_Env::InfoEnv.new(@md.fns) #@env=@particulars.env
    end
    def directories
      begin
        case @opt.fns
        when /\.(?:-|ssm\.)?sst$/
          SiSU_Env::FileOp.new(@md).mkdir
          Dir.mkdir(@env.processing_path.tex) unless FileTest.directory?(@env.processing_path.tex)
        end
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.cmd,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
      end
    end
    def read
      song
    end
    def song
      begin
        @md=@particulars.md
        SiSU_Screen::Ansi.new(@opt.act[:color_state][:set],'LaTeX/PDF',"[#{@opt.f_pth[:lng_is]}] #{@opt.fno}").green_title_hi unless @opt.act[:quiet][:set]==:on
        if (@opt.act[:verbose][:set]==:on \
        || @opt.act[:verbose_plus][:set]==:on \
        || @opt.act[:maintenance][:set]==:on)
          @env.url.output_tell
          if @md.opt.act[:pdf_l][:set]==:on
            SiSU_Screen::Ansi.new(@opt.act[:color_state][:set],@opt.fns,"#{@env.program.pdf_viewer} #{@md.file.output_path.pdf.dir}/#{@md.file.base_filename.pdf_l}pdf").flow
          end
          if @md.opt.act[:pdf_p][:set]==:on
            SiSU_Screen::Ansi.new(@opt.act[:color_state][:set],@opt.fns,"#{@opt.fns} #{@env.program.pdf_viewer} #{@md.file.output_path.pdf.dir}/#{@md.file.base_filename.pdf_p}pdf").flow
          end
        end
        @md=@particulars.md
        $flag=@md.opt.cmd                                                          #introduced to pass 0 for no object citation numbers... to texpdf_format
        directories
                                                                               #% needed needs to be reprogrammed !!!
        ao_array=SiSU_AO::Source.new(@opt).get # ao file drawn here
        SiSU_TeX::Source::LaTeXcreate.new(@particulars).songsheet
        ao_array=''
        pwd=Dir.pwd
        SiSU_TeX::Source::LaTeXtoPdf.new(@md,@particulars.env).latexrun_selective
        Dir.chdir(pwd)
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.cmd,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
        unless (@opt.act[:verbose_plus][:set]==:on \
        || @opt.act[:maintenance][:set]==:on)
          texfiles=Dir["#{@env.processing_path.tex}/#{@opt.fns}*"]
          texfiles.each do |f|
            if FileTest.file?(f)
              File.unlink(f)
            end
          end
        end
        @tex_file=@@tex_footnote_array=[]
        @@column_instruct=''
        @@squigle_close=@@tex_line_mode=@@tex_word_mode=@@line_mode=''
        @@tex_debug_counter=@@table_pagebreak_counter=@@tex_footnote_call_counter=@@tex_table_flag=@@tex_counter=@@tex_column=@@tex_columns=@@tex_columns=@@counting=0
        @@tex_col_w=[]
        @@n=@@tableheader=@@rights=nil
        @@date=SiSU_Env::InfoDate.new
        @@flag={}
        $flag=1 #remove at some stage
        SiSU_Env::Clear.new(@opt.cmd,@opt.fns).param_instantiate
      end
    end
    private
    class LaTeXtoPdf
      @@n_lpdf||=0 #change
      def initialize(md,env)
        @md,@env=md,env
        @f=SiSU_Env::FileOp.new(@md).base_filename
      end
      def latex_do(texfilename,papersize)
        @texfilename=texfilename
        @@n_lpdf=@@n_lpdf+1
        tex_fn_base=@texfilename.gsub(/\.tex$/,'')
        tell=SiSU_Screen::Ansi.new(@md.opt.cmd)
        if @md.opt.act[:pdf_p][:set]==:on
          if (@md.opt.act[:verbose][:set]==:on \
          || @md.opt.act[:verbose_plus][:set]==:on \
          || @md.opt.act[:maintenance][:set]==:on)
            SiSU_Screen::Ansi.new(@md.opt.act[:color_state][:set],"#{papersize} portrait ->").dark_grey_title_hi
          end
          cmd=SiSU_Env::SystemCall.new("#{tex_fn_base}.tex",'',@md.opt.cmd)
          tell.grey_open if @md.opt.cmd =~/[MVv]/
          if "#{tex_fn_base}" =~/\w+/ \
          and "#{papersize}" =~/\w+/
            2.times { |i| cmd.latex2pdf(@md,papersize) } #comment out to skip processing of latex portrait
          end
          tell.p_off if @md.opt.cmd =~/[MVv]/
        end
        if @md.opt.act[:pdf_l][:set]==:on
          if (@md.opt.act[:verbose][:set]==:on \
          || @md.opt.act[:verbose_plus][:set]==:on \
          || @md.opt.act[:maintenance][:set]==:on)
            SiSU_Screen::Ansi.new(@md.opt.act[:color_state][:set],"#{papersize} landscape ->").dark_grey_title_hi
          end
          cmd=SiSU_Env::SystemCall.new("#{tex_fn_base}.landscape.tex",'',@md.opt.cmd)
          if (@md.opt.act[:verbose][:set]==:on \
          || @md.opt.act[:verbose_plus][:set]==:on \
          || @md.opt.act[:maintenance][:set]==:on)
            tell.grey_open
          end
          if "#{tex_fn_base}" =~/\w+/ \
          and "#{papersize}" =~/\w+/
            2.times { |i| cmd.latex2pdf(@md,papersize) } #comment out to skip processing of latex landscape
          end
          if (@md.opt.act[:verbose][:set]==:on \
          || @md.opt.act[:verbose_plus][:set]==:on \
          || @md.opt.act[:maintenance][:set]==:on)
            tell.p_off
          end
        end
        pwd=Dir.pwd
        if @md.opt.act[:pdf_p][:set]==:on
          portrait_pdf="#{pwd}/#{tex_fn_base}.pdf"
        end
        if @md.opt.act[:pdf_l][:set]==:on
          landscape_pdf="#{pwd}/#{tex_fn_base}.landscape.pdf"
        end
        case papersize
        when /a4/;     pdf_p=@f.pdf_p_a4;     pdf_l=@f.pdf_l_a4
        when /a5/;     pdf_p=@f.pdf_p_a5;     pdf_l=@f.pdf_l_a5
        when /b5/;     pdf_p=@f.pdf_p_b5;     pdf_l=@f.pdf_l_b5
        when /letter/; pdf_p=@f.pdf_p_letter; pdf_l=@f.pdf_l_letter
        when /legal/;  pdf_p=@f.pdf_p_legal;  pdf_l=@f.pdf_l_legal
        else           pdf_p=@f.pdf_p_a4;     pdf_l=@f.pdf_l_a4
        end
        FileUtils::mkdir_p(@md.file.output_path.pdf.dir) unless FileTest.directory?(@md.file.output_path.pdf.dir)
        cX=SiSU_Screen::Ansi.new(@md.opt.act[:color_state][:set]).cX
        if @md.opt.act[:pdf_p][:set]==:on
          if FileTest.file?(portrait_pdf)
            FileUtils::cp(portrait_pdf,"#{@md.file.output_path.pdf.dir}/#{pdf_p}")
            FileUtils::rm(portrait_pdf)
          else
            STDERR.puts "#{cX.fuchsia}pdf file not generated#{cX.off} <#{cX.blue}#{portrait_pdf.gsub(/.+?([^\/]+?\.pdf)$/,'\1')}#{cX.off}> (check texlive dependencies)"
            STDERR.puts "#{__FILE__}:#{__LINE__} NOT FOUND: #{portrait_pdf}" if @md.opt.act[:maintenance][:set]==:on
          end
        end
        if @md.opt.act[:pdf_l][:set]==:on
          if FileTest.file?(landscape_pdf)
            FileUtils::cp(landscape_pdf,"#{@md.file.output_path.pdf.dir}/#{pdf_l}")
            FileUtils::rm(landscape_pdf)
          else
            STDERR.puts "#{cX.fuchsia}pdf file not generated#{cX.off} <#{cX.blue}#{landscape_pdf.gsub(/.+?([^\/]+?\.pdf)$/,'\1')}#{cX.off}> (check texlive dependencies)"
            STDERR.puts "#{__FILE__}:#{__LINE__} NOT FOUND: #{landscape_pdf}" if @md.opt.act[:maintenance][:set]==:on
          end
        end
        if (@md.opt.act[:verbose][:set]==:on \
        || @md.opt.act[:verbose_plus][:set]==:on \
        || @md.opt.act[:maintenance][:set]==:on)
          SiSU_Screen::Ansi.new(@md.opt.act[:color_state][:set],@@n_lpdf,'processed (SiSU LaTeX to pdf - using pdfetex aka. pdftex or pdflatex)').generic_number
        end
      end
      def latexrun_selective
        begin
          pwd=Dir.pwd
          Dir.chdir(pwd) #watch
          @tex_f_no=0
          if FileTest.file?(@env.source_file_with_path)
            @md.papersize_array.each do |ps|
              if @md.fns =~/\.(?:-|ssm\.)?sst$/
                case @md.fns
                when /\.(?:-|ssm\.)?sst$/
                  if FileTest.directory?(@env.processing_path.tex)==true
                    Dir.chdir(@env.processing_path.tex)
                    texfile=@md.fns.gsub(/$/,".#{ps}.tex").
                      gsub(/~/,'-')
                    if @md.opt.act[:pdf_p][:set]==:on \
                    or @md.opt.act[:pdf_l][:set]==:on
                      latex_do(texfile,ps)
                      if @md.opt.act[:pdf_p][:set]==:on
                        if File.exist?(texfile) \
                        and File.size(texfile) > 0
                          #@tex_f_no+=1
                        else
                          SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:fuchsia).mark("\tzero file size #{@env.processing_path.tex}/#{texfile}")
                        end
                      end
                    end
                  end
                end
              end
            end
            case @md.papersize_array[0] #default pdf
            when /a4/;     pdf_p=@f.pdf_p_a4;     pdf_l=@f.pdf_l_a4
            when /a5/;     pdf_p=@f.pdf_p_a5;     pdf_l=@f.pdf_l_a5
            when /b5/;     pdf_p=@f.pdf_p_b5;     pdf_l=@f.pdf_l_b5
            when /letter/; pdf_p=@f.pdf_p_letter; pdf_l=@f.pdf_l_letter
            when /legal/;  pdf_p=@f.pdf_p_legal;  pdf_l=@f.pdf_l_legal
            else           pdf_p=@f.pdf_p_a4;     pdf_l=@f.pdf_l_a4
            end
            if @md.opt.act[:pdf_p][:set]==:on
              if FileTest.file?("#{@md.file.output_path.pdf.dir}/#{pdf_p}")
                mklnk=((@md.file.output_dir_structure.by_language_code?) \
                || (@md.file.output_dir_structure.by_filetype?)) \
                ? "#{@md.fnb}.portrait.pdf"
                : 'portrait.pdf'
                if FileTest.directory?(@md.file.output_path.pdf.dir)
                  pwd=Dir.pwd
                  Dir.chdir(@md.file.output_path.pdf.dir)
                  FileUtils::rm_f(mklnk)
                  FileUtils::ln_s(pdf_p, mklnk)
                  Dir.chdir(pwd)
                end
              end
            end
            if @md.opt.act[:pdf_l][:set]==:on
              if FileTest.file?("#{@md.file.output_path.pdf.dir}/#{pdf_l}")
                mklnk=((@md.file.output_dir_structure.by_language_code?) \
                || (@md.file.output_dir_structure.by_filetype?)) \
                ? "#{@md.fnb}.landscape.pdf"
                : 'landscape.pdf'
                pwd_set=Dir.pwd
                Dir.chdir(@md.file.output_path.pdf.dir)
                FileUtils::rm_f(mklnk)
                FileUtils::ln_s(pdf_l, mklnk)
                Dir.chdir(pwd_set)
              end
            end
          else
            SiSU_Screen::Ansi.new(@md.opt.act[:color_state][:set],"*WARN* FILE NOT FOUND: << #{@md.fns} >> - requested latex system processing skipped").warn
          end
          lst=Dir["*.{aux,log,out}"]
          lst.each {|file| File.unlink(file)} if lst
        rescue
          SiSU_Errors::Rescued.new($!,$@,@md.opt.cmd,@md.fns).location do
            __LINE__.to_s + ':' + __FILE__
          end
        end
      end
    end
    class LaTeXcreate
      @@tex_head={
        'a4'=>    { p: nil, l: nil },
        'a5'=>    { p: nil, l: nil },
        'b5'=>    { p: nil, l: nil },
        'letter'=>{ p: nil, l: nil },
        'legal'=> { p: nil, l: nil },
        'book'=>  { p: nil, l: nil }
      }
      @@prefix_b=nil
      def initialize(particulars)
        @particulars=particulars
        @md=@particulars.md
        @env=SiSU_Env::InfoEnv.new(@md.fns) #@env=@particulars.env
        @data=@particulars.ao_array # ao file drawn here
        @st={ tex: {} }
        @tex_ml=SiSU_TeX_Pdf::UseTeX.new(@md)
        @vz=SiSU_Viz::Defaults.new
        @dp=@@dp ||=SiSU_Env::InfoEnv.new.digest.pattern
        @brace_url=SiSU_Viz::Defaults.new.url_decoration
        l=SiSU_Env::StandardiseLanguage.new(@md.opt.lng).language
        @language=l[:n]
        @translate=SiSU_Translate::Source.new(@md,@language)
        @codeblock_box='listings' #alternative 'boites'
        @make ||=SiSU_Env::ProcessingSettings.new(@md)
      end
      def songsheet
        begin
          data=@data
          @@tex_footnote_array=[]
          @@rights=nil
          txt_gen=if @md.opt.act[:pdf_l][:set]==:on \
          and @md.opt.act[:pdf_p][:set]==:on
            'pdfTex portrait & landscape'
          elsif @md.opt.act[:pdf_l][:set]==:on
            'pdfTex landscape'
          elsif @md.opt.act[:pdf_p][:set]==:on
            'pdfTex portrait'
          else
            SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:fuchsia).mark('error: neither landscape nor portrait')
          end
          if (@md.opt.act[:verbose][:set]==:on \
          || @md.opt.act[:verbose_plus][:set]==:on \
          || @md.opt.act[:maintenance][:set]==:on)
            SiSU_Screen::Ansi.new(@md.opt.act[:color_state][:set],txt_gen).txt_grey
          end
          if defined? @md.rights.all \
          and not @md.rights.all.empty?
            rght=@md.rights #.author.dup #dup is necessary, else contents of :rights changed
            sp_char=SiSU_TeX_Pdf::SpecialCharacters.new(@md,rght.copyright.all)
            copymark=@md.author_copymark \
            ? '{\begin{small}\copyright\end{small}} '
            : ''
            copymark='Copyright {\begin{small}\copyright\end{small}} '
            copyright=sp_char.special_characters_safe.gsub(/^\s*Copyright \(C\)/, copymark)
            @@rights||="\n #{Tex[:backslash]*2}[3]\\ \\linebreak #{copyright}"
          end
          if defined? @md.notes.prefix_b \
          and not @md.notes.prefix_b.empty?
            sp_char=SiSU_TeX_Pdf::SpecialCharacters.new(@md,@md.notes.prefix_b)
            prefix_b=sp_char.special_characters_safe
            @@prefix_b="\n #{Tex[:backslash]*2}[3]\\ \\linebreak \\ #{prefix_b}\n" unless @@prefix_b
          end
          data=pre(data)
          data=footnote(data)
          if @md.flag_tables #WORK ON 2009
            data=tables(data) #uncomment to start experimenting with tables
          end
          data=number_paras(data)
          data=markup(data)
          output(data)
        rescue
          SiSU_Errors::Rescued.new($!,$@,@md.opt.cmd,@md.fns).location do
            __LINE__.to_s + ':' + __FILE__
          end
        ensure
        end
      end
    protected
      def pre(data)
        @tex_file=[]
        data.each do |dob|
          # DEBUG 2003w16 this is a kludge, because i could not get parameters
          # from param, Sort out ... revert to more elegant solution
          # even more of a kludge as had to insert newlines where code is used not satisfactory, think about
          dob.tmp=dob.obj #.dup
          if dob.is==:para \
          || dob.is==:heading
            dob.tmp=dob.tmp.gsub(/#{Mx[:mk_o]}:name#\S+?#{Mx[:mk_c]}/,'')
            dob.tmp=SiSU_TeX_Pdf::SpecialCharacters.new(@md,dob.tmp).special_characters
            if dob.tmp =~/#{Mx[:lnk_o]}.+?#{Mx[:lnk_c]}#{Mx[:rel_o]}\S+?#{Mx[:rel_c]}/
              dob.tmp=SiSU_TeX_Pdf::FormatTextObject.new(@md,dob.tmp).url_str_internal(dob.tmp)
            end
          elsif dob.is ==:code
            dob.tmp=if @codeblock_box=='listings'
              dob.tmp
            else
              SiSU_TeX_Pdf::SpecialCharacters.new(@md,dob.tmp).special_characters_code
            end
          elsif dob.is ==:break
            if dob.obj==Mx[:br_page]; dob.tmp='\newpage'
            elsif dob.obj==Mx[:br_page_new]; dob.tmp='\clearpage'
            elsif dob.obj==Mx[:br_page_line]; dob.tmp=' \\ \hline \\ '
            elsif dob.obj==Mx[:br_obj]; dob.tmp='\parasep'
            end
          elsif dob.is==:comment \
          || dob.is==:meta
            dob.tmp='' #dob.tmp=nil
          end
        end
        data
      end
      def footnote(data)
        data.each do |dob|
          # EMBEDDED FOOTNOTES / ENDNOTES should be straightforward but not quite a synch.
          if dob.tmp =~/#{Mx[:en_a_o]}[\d*+]+\s|#{Mx[:en_b_o]}([*+]\d+)\s/
            dob.tmp=dob.tmp.gsub(/#{Mx[:en_a_o]}(\d+)\s+(.+?)#{Mx[:en_a_c]}/m,"\\footnote[\\1]{%\n \\2} ").
              gsub(/#{Mx[:en_b_o]}([*+]\d+)\s+(.+?)#{Mx[:en_b_c]}/m,"\\FootnoteA{\\1}{%\n \\2} ").
              gsub(/#{Mx[:en_a_o]}([*+]+)\s+(.+?)#{Mx[:en_a_c]}/m,"\\FootnoteA{\\1}{%\n \\2} ")
          end
        end
        data
      end
      def tables_hash(md,dob)
        @block={}
        @dob=dob
        @md.papersize_array.each do |ps|
          @@tableheader={ ps => { p: 0, l: 0 } }
          dob.tmp={ tmp: dob.tmp, paper_size: ps }
          format_l=SiSU_TeX_Pdf::FormatTextObject.new(md,dob)
          dob.tmp={ tmp: dob.tmp, paper_size: ps }
          format_p=SiSU_TeX_Pdf::FormatTextObject.new(md,dob)
          @block[ps]={
            l: format_l.longtable_landscape,
            p: format_p.longtable_portrait
          }
        end
        @dob.tmp=@block
        @dob
      end
      def tables(data)
        @tex_file=[]
        data.each do |dob|
          @tex_file << if dob.is_a?(String) \
          or dob.is_a?(Hash)
            dob
          elsif dob.is==:table
            tables_hash(@md,dob) #Hash result
          else dob
          end
        end
        @tex_file
      end
      def enclose(dob)
        dob
      end
      def box_boites(dob,ocn)
        sp_char=SiSU_TeX_Pdf::SpecialCharacters.new(@md,dob.tmp,dob.is)
        dob.tmp=sp_char.special_characters_safe
        dob.tmp=dob.tmp.gsub(/(#{Mx[:nbsp]})/m,'{\color{mywhite}\1}').
        #dob.tmp.gsub(/#{Mx[:nbsp]}/m,'{~}') # dob.tmp.gsub(/#{Mx[:nbsp]}\s*/m,'{~}')
          gsub(/#{Mx[:vline]}/m,'\vline').
          gsub(/ \\( |#{Mx[:br_nl]})/,' {\textbackslash}\1').
          gsub(/#{Mx[:br_nl]}\s*\Z/m,'').
          gsub(/#{Mx[:br_nl]}{2}/,'\newline \\\\\\ ').
          gsub(/#{Mx[:br_nl]}/,' \\\\\\ ').
          gsub(/\n\n\n/m," \\newline\n\n")
        ocn=SiSU_TeX_Pdf::FormatTextObject.new(@md).ocn_display(dob)
        dob.tmp = ocn \
        + @tex_ml.paraskip_small \
        + '\begin{Codeblock}' \
        + '\begin{codeblockboitebox} \hardspace \newline ' \
        + dob.tmp \
        + '\end{codeblockboitebox}' \
        + '\end{Codeblock}' \
        + "\n" \
        + @tex_ml.paraskip_normal
        dob
      end
      def box_listings(dob,ocn)
        sp_char=SiSU_TeX_Pdf::SpecialCharacters.new(@md,dob.tmp,dob.is)
        dob.tmp=sp_char.characters_code_listings
        dob.tmp=dob.tmp.gsub(/^\s+/m,''). #bug, fix earlier, should be made unecessary
          gsub(/#{Mx[:nbsp]}/m,' ').
          gsub(/#{Mx[:vline]}/m,'|').
          gsub(/#{Mx[:br_nl]}(?:\s?\n)?/m,"\n").
          gsub(/\n\n\n/m," \n\n")
        ocn=SiSU_TeX_Pdf::FormatTextObject.new(@md).ocn_display(dob)
        dob.tmp = ocn \
        + @tex_ml.paraskip_small \
        + '\begin{Codeblock}' \
        + "\n" \
        + '\begin{lstlisting} ' \
        + "\n" \
        + dob.tmp \
        + "\n" \
        + '\end{lstlisting} ' \
        + "\n" \
        + '\end{Codeblock}' \
        + "\n" \
        + @tex_ml.paraskip_normal
        dob
      end
      def markup_common(dob)
        if dob.of==:block
          @lineone=if dob.is==:block \
          || dob.is==:group \
          || dob.is==:alt \
          || dob.is==:verse
            dob.tmp=dob.tmp.gsub(/#{Mx[:nbsp]}/m,' \hardspace ').
              gsub(/#{Mx[:gl_bullet]}/m,'\txtbullet \hardspace '). #Bullet environment not used for grouped text, no hanging indent here
              gsub(/#{Mx[:br_nl]}+/m,"\n\n") #match not ideal, but currently not inserting extra newlines anyway
            ocn=SiSU_TeX_Pdf::FormatTextObject.new(@md).ocn_display(dob)
            dob.tmp=if dob.is==:group \
            || dob.is==:block \
            || dob.is==:alt
              dob.tmp=SiSU_TeX_Pdf::SpecialCharacters.new(@md,dob.tmp).special_characters_safe
              ocn \
              + @tex_ml.paraskip_small \
              + "\n" \
              + ' \\begin{footnotesize}' \
              + "\n\n" \
              + dob.tmp \
              + '\\end{footnotesize}' \
              + "\n" \
              + @tex_ml.paraskip_normal
            elsif dob.is==:verse
              dob.tmp=dob.tmp.gsub(/#{Mx[:fa_bold_o]}(.+?)#{Mx[:fa_bold_c]}/m,'\begin{bfseries}\1 \end{bfseries}').
                gsub(/#{Mx[:fa_italics_o]}(.+?)#{Mx[:fa_italics_c]}/m,'\emph{\1}').
                gsub(/#{Mx[:fa_underscore_o]}(.+?)#{Mx[:fa_underscore_c]}/m,'\uline{\1}')
              ocn \
              + @tex_ml.paraskip_tiny \
              + "\n" \
              + ' \\begin{footnotesize}' \
              + "\n\n" \
              + dob.tmp \
              + '\\end{footnotesize}' \
              + "\n" \
              + @tex_ml.paraskip_normal \
              + "\n\\linebreak\n"
            end
            dob
          elsif dob.is ==:code
            dob=if @codeblock_box == 'listings'
              box_listings(dob,ocn)
            elsif @codeblock_box == 'boites'
              box_boites(dob,ocn)
            else
              box_boites(dob,ocn)
            end
            dob
          else 'error' #should never occur
          end
          dob=enclose(dob) unless dob.tmp =~/^$/
          dob
        else
          tst=SiSU_TeX_Pdf::FormatTextObject.new(@md,dob)
          case dob.is
          when :heading
            case dob.ln
            when 1..3
              tst.heading_major
            when 4
              tst.level4
            when 5
              tst.level5
            when 6
              tst.level6
            else dob
            end
          when :heading_insert
            br="\n\\\\\n"
            if dob.name=='book_index'
              h=tst.heading_major
              heading="\\clearpage\n" + h.tmp
              idx_arr=[]
              idx=SiSU_Particulars::CombinedSingleton.instance.get_idx_raw(@md.opt).raw_idx
              idx.each do |x|
                x=if x.is_a?(String)
                  x=SiSU_TeX_Pdf::SpecialCharacters.new(@md,x).special_characters
                  x=SiSU_TeX_Pdf::FormatTextObject.new(@md,x).url_str_internal(x,true)
                else x=nil
                end
                idx_arr << x.sub(/,$/,'') if x.is_a?(String)
              end
              idx_str=idx_arr.join(br)
              l=heading + br + idx_str
              p=heading + br +
                '\begin{multicols}{2}' + br +
                idx_str + br +
                '\end{multicols}'
              dob.tmp={ l: l, p: p }
            elsif dob.ln==2 \
            and dob.obj=~/Metadata\b/
              tst.heading_major
            elsif dob.ln==4 \
            and dob.obj=~/Metadata\b/
              h=tst.level4
              metadata=SiSU_Metadata::TeX_Metadata.new(@md).metadata_tex
              dob.tmp=h.tmp + ' ' + '\begin{scriptsize}' + metadata.join(br) + '\end{scriptsize}'
            else dob.tmp='' # dob.tmp={ l: '', p: '' }
            end
          when :para
            if dob.bullet_
              dob.tmp=tst.bullet
            elsif dob.indent \
            and dob.hang \
            and dob.indent =~/[1-9]/ \
            and dob.indent == dob.hang
              dob.tmp=tst.indent
            elsif dob.hang \
            and dob.hang =~/[0-9]/ \
            and (dob.indent != dob.hang or dob.indent =~/[1-9]/)
              dob.tmp=tst.hang
            else
              dob.tmp=dob.tmp.strip
              dob=enclose(dob) unless dob.tmp =~/^$/
            end
          else
            dob.tmp=dob.tmp.strip unless dob.is==:code
            dob=enclose(dob) unless dob.tmp =~/^$/
          end
          if dob.is_a?(String)
            dob.tmp=dob.tmp.gsub(/\s*(?:#{Mx[:br_line]}|#{Mx[:br_nl]})\s*/,' \newline ').   #% tread with care
              gsub(/(\.#{Tex[:tilde]}\S*\s*|<:\S+>|#{Mx[:fa_o]}.*?#{Mx[:fa_c]}|#{Mx[:gr_o]}.*?#{Mx[:gr_c]}|<!.*?!>|<!>)/,' ')   #% tread with care
          end
          dob
        end
        if dob.tmp =~/(?:#{Mx[:url_o]}\S+?#{Mx[:url_c]}|image\b)/m \
        && dob.is !=:code
          dob=SiSU_TeX_Pdf::BareUrls.new(@md,dob).bare_urls
          tst=SiSU_TeX_Pdf::FormatTextObject.new(@md,dob)
          dob=tst.urls_txt_and_images
          dob
        elsif dob.tmp =~/https?:\/\/\S+\b/m \
        && dob.is ==:code \
        && @codeblock_box !='listings'
          dob=SiSU_TeX_Pdf::BareUrls.new(@md,dob).bare_urls_in_code
          dob
        end
        if dob.class !=Hash \
        && dob.tmp =~/#{Mx[:lnk_o]}.+?#{Mx[:lnk_c]}image\b/ \
        && dob.is !=:code
          tst=SiSU_TeX_Pdf::FormatTextObject.new(@md,dob)
        end
        dob
      end
      def tex_box_listings
        <<-WOK
\\definecolor{listinggray}{gray}{0.9}
\\definecolor{lbcolor}{rgb}{0.9,0.9,0.9}
\\lstset{
	backgroundcolor=\\color{lbcolor},
	tabsize=4,
	rulecolor=,
	language=,
  basicstyle=\\scriptsize,
  upquote=true,
  aboveskip={1.5\\baselineskip},
  columns=fixed,
  showstringspaces=false,
  extendedchars=true,
  breaklines=true,
  prebreak = \\raisebox{0ex}[0ex][0ex]{\\ensuremath{\\hookleftarrow}},
  frame=single,
  showtabs=false,
  showspaces=false,
  showstringspaces=false,
  identifierstyle=\\ttfamily,
  keywordstyle=\\color[rgb]{0,0,1},
  commentstyle=\\color[rgb]{0.133,0.545,0.133},
  stringstyle=\\color[rgb]{0.627,0.126,0.941},
}
        WOK
      end
      def tex_box_boites
        <<-WOK
\\def\\codeblockboitebox{%
  \\def\\bkvz@before@breakbox{\\ifhmode\\par\\fi\\vskip\\breakboxskip\\relax}%
  \\def\\bkvz@set@linewidth{\\advance\\linewidth -2\\fboxrule
    \\advance\\linewidth -2\\fboxsep} %
  \\def\\bk@line{\\hbox to \\linewidth{%
      \\ifbkcount\\smash{\\llap{\\the\\bk@lcnt\\ }}\\fi
      \\psframebox*[framesep=0pt,linewidth=0pt]{%
        \\vrule\\@width\\fboxrule \\hskip\\fboxsep
        \\box\\bk@bxa
        \\hskip\\fboxsep \\vrule\\@width\\fboxrule
        }%
      }}%
  %\\def\\bkvz@top{\\hrule\\@height\\fboxrule}
  \\def\\bkvz@top{\\hrule height .6pt}%
  \\def\\bkvz@bottom{\\hrule\\@height\\fboxrule}%
  \\breakbox}
\\def\\endcodeblockboitebox{\\endbreakbox}
        WOK
      end
      def tex_codeblock
        codeblock_box=if @codeblock_box=='listings'
          tex_box_listings
        elsif @codeblock_box=='boites'
          tex_box_boites
        else
          tex_box_boites
        end
        codeblock_box
      end
      def markup(data)
        @tex_file=[]
        home=@vz.txt_home.gsub(/#{Mx[:br_line]}|#{Mx[:br_nl]}|#{Mx[:br_paragraph]}|\\\\/,' - ') #no line splitting in heading neither html nor latex
        title=@md.title.full.gsub(/#{Mx[:br_line]}|#{Mx[:br_nl]}|#{Mx[:br_paragraph]}|\\\\/,' - ') #no line splitting in heading neither html nor latex
        @md.papersize_array.each do |ps|
          if @md.opt.act[:pdf_p][:set]==:on
            txt_obj={ txt: "#{home}: - #{title}", paper_size: ps, orientation: 'portrait' }
            orient_portrait=SiSU_TeX_Pdf::FormatHead.new(@md,txt_obj)
            @@tex_head[ps][:p]=orient_portrait.document_head_with_orientation(@codeblock_box)
          end
          if @md.opt.act[:pdf_l][:set]==:on
            txt_obj={ txt: "#{home}: - #{title}", paper_size: ps, orientation: 'landscape' }
            orient_landscape=SiSU_TeX_Pdf::FormatHead.new(@md,txt_obj)
            @@tex_head[ps][:l]=orient_landscape.document_head_with_orientation(@codeblock_box)
          end
        end
        @tex_file << <<-WOK
#{@tex_ml.header}#{@tex_ml.footer}
\\tolerance=300
\\clubpenalty=300
\\widowpenalty=300
\\makeatother
\\makeatother
\\chardef\\txtbullet="2022
\\chardef\\tilde="7E
%\\chardef\\asterisk="2A
\\def\\asterisk{{\\rm \\char42} }
\\definecolor{Light}{gray}{.92}
\\newcommand{\\Codeblock}[1]{\\normaltext\\raggedright\\small\\ttfamily\\texbackslash#1}
\\newcommand{\\monosp}[1]{\\normaltext\\ttfamily\\texbackslash#1}
\\newcommand{\\parasep}{\\\\ \\begin{center}*\\hspace{2em}*\\hspace{2em}*\\end{center} \\\\}
\\newcommand{\\hardspace}{{~}}
%\\newcommand{\\hardspace}{\\hspace{.5em}}
\\newcommand{\\caret}{{\\^{~}}}
\\newcommand{\\pipe}{{\\textbar}}
\\newcommand{\\curlyopen}{\{}
\\newcommand{\\curlyclose}{\}}
\\newcommand{\\lt}{{\UseTextSymbol{OML}{<}}}
\\newcommand{\\gt}{{\UseTextSymbol{OML}{>}}}
\\newcommand{\\slash}{{/}}
\\newcommand{\\underscore}{\\_}
\\newcommand{\\exclaim}{\\Verbatim{!}}
#{tex_codeblock}
% (tilde hash amp affected by http)
% \\sloppy
\\begin{document}
        WOK
        @copymark='' #check and remove as now is superflous
        x={}
        txt_obj={ title: @md.title.full }
        if @md.opt.act[:pdf_l][:set]==:on
          x[:l]=SiSU_TeX_Pdf::FormatTextObject.new(@md,txt_obj).title_landscape
        end
        if @md.opt.act[:pdf_p][:set]==:on
          x[:p]=SiSU_TeX_Pdf::FormatTextObject.new(@md,txt_obj).title_portrait
        end
        @tex_file << x
        x=nil
        if defined? @md.creator.author \
        and @md.creator.author
          sp_char=SiSU_TeX_Pdf::SpecialCharacters.new(@md,@md.creator.author)
          author=sp_char.special_characters
          @tex_file << if @md.author_home
            <<-WOK

\\author{\\href{#{@md.author_home}}{#{@copymark} \\textnormal{#{author}}}}
            WOK
          else "\n\\author{#{@copymark} \\textnormal{#{author}}}"
          end
        end
        if defined? @md.make.cover_image \
        and not @md.make.cover_image.nil? \
        and @md.make.cover_image[:cover] =~/\S+/
          x={}
          dir=SiSU_Env::InfoEnv.new(@md.fns)
          x[:l] =<<-WOK
\\titlepic{\\includegraphics[width=0.3\\textwidth]{#{dir.path.image_source_include}/#{@md.make.cover_image[:cover]}}}
          WOK
          x[:p] =<<-WOK
\\titlepic{\\includegraphics[width=0.6\\textwidth]{#{dir.path.image_source_include}/#{@md.make.cover_image[:cover]}}}
          WOK
          @tex_file << x
          x=nil
        end
        @tex_file << unless @md.fnb =~/^mail\s*$/ then @tex_ml.site
        else                                           '\date'
        end
        @tex_file << <<-WOK
\\pagenumbering{roman}\\maketitle
\\pagestyle{fancy}
        WOK
        if defined? @md.rights.all \
        and @md.rights.all
          @tex_file << "\\newpage\n"
          @tex_file << @@rights
          @tex_file << @@prefix_b if defined? @md.creator.prefix_b and @md.creator.prefix_b
        end
        x={}
        if (@make.build.toc?)
          toc=<<-WOK
\\renewcommand{\\contentsname}{#{@translate.contents}}
\\tableofcontents
          WOK
          toc_pb={ l: @tex_ml.newpage('landscape'), p: @tex_ml.newpage('portrait') }
        else
          toc=''
          toc_pb={ l: '', p: '' }
        end
        if @md.opt.act[:pdf_l][:set]==:on
          x[:l] =<<-WOK
#{@tex_ml.newpage('landscape')}
\\pagestyle{fancy}
#{toc}#{toc_pb[:l]}
\\pagenumbering{arabic}
#{@tex_ml.paraskip_normal}
#{@tex_ml.newpage('landscape')}
          WOK
        end
        if @md.opt.act[:pdf_p][:set]==:on
          x[:p] =<<-WOK
#{@tex_ml.newpage('portrait')}
\\pagestyle{fancy}
#{toc}#{toc_pb[:p]}
#{@tex_ml.newpage('portrait')}
\\pagenumbering{arabic}
#{@tex_ml.paraskip_normal}
#{@tex_ml.newpage('portrait')}
          WOK
        end
        @tex_file << x
        x=nil
        data.each do |dob|                                                      #% case follows with levels 1-6 indents & graphics
          if dob.is_a?(Hash)
          elsif dob.of==:para \
          || dob.of==:block #GATEWAY FIX FIX stuff
            dob=markup_common(dob)
          elsif dob.is==:table
            if ( dob.tmp['a4'] \
            or dob.tmp['a5'] \
            or dob.tmp['b5'] \
            or dob.tmp['letter'] \
            or dob.tmp['legal'])
              @md.papersize_array.each do |ps|
                if dob.tmp[ps]
                  if (dob.tmp[ps][:p] and dob.tmp[ps][:l])
                    dob.tmp[ps]={
                      p: markup_common(dob.tmp[ps][:p]),
                      l: markup_common(dob.tmp[ps][:l])
                    }
                  else p "#{__FILE__}:#{__LINE__}" if @md.opt.act[:maintenance][:set]==:on
                  end
                end
              end
            elsif dob.tmp.is_a?(Hash) \
            and (dob.tmp[:p] and dob.tmp[:l])
              dob = {
                p: markup_common(dob.tmp[:p]),
                l: markup_common(dob.tmp[:l])
              }
            else p "#{__FILE__}:#{__LINE__}" if @md.opt.act[:maintenance][:set]==:on
            end
          end
          @tex_file << dob
        end
        @st[:tex][:stmp]||=@md.stmpd
        stamp=@st[:tex][:stmp] if @st[:tex][:stmp]
        if stamp
          use=stamp.gsub(/\n/,"#{Tex[:backslash]*2}\n")
          @tex_file << "\n\\newpage\n"
          @tex_file << "\\section*" +
            "{#{@tex_ml.owner_chapter}}\n" +
            "\\addcontentsline{toc}" +
            "{section}{#{@tex_ml.owner_chapter}}\n"
          @tex_file << "#{use}\n"
          @tex_file << @@rights if @@rights
        end
        @tex_file << "\n\\end{document}"
      end
      def number_paras_numbering(dob) # need tables and other types of object
        if dob.of ==:para
          paranum=dob.ocn ? dob.ocn : ''
          paranum = '' if paranum.to_i==0
          paranumber_display=if @make.build.ocn?
            tags=''
            #[keep] code that follows inserts "name tags" as hypertargets, currently using ocn (converting nametags to ocn) for internal linking, related code: |texpdf_format.rb|@|uses nametags directly|
            #if dob.tags.length > 0 # insert tags "hypertargets"
            #  dob.tags.each do |t|
            #    tags=tags +"\\hspace{0mm}\\hypertarget{#{t}}{\\hspace{0mm}}"
            #  end
            #end
            "\\begin{tiny}\\hspace{0mm}\\end{tiny}{\\marginpar{\\begin{tiny}\\hspace{0mm}\\hypertarget{#{dob.ocn}}{#{dob.ocn}}#{tags}\\end{tiny}}}" #ocn object citation numbering
          else ''
          end
          dob.tmp = paranumber_display + dob.tmp
        end
        dob
      end
      def number_paras(data)
        data.each do |dob|
          dob=if dob.is_a?(Hash)
            if ( dob['a4'] \
            or dob['a5'] \
            or dob['b5'] \
            or dob['letter'] \
            or dob['legal'])
              para_hash={}
              @md.papersize_array.each do |ps|
                if defined? dob.tmp and dob.tmp[ps]
                  if (dob.tmp[ps][:p] and dob.tmp[ps][:l])
                    para_hash[ps]={
                      p: number_paras_numbering(dob.tmp[ps][:p]),
                      l: number_paras_numbering(dob.tmp[ps][:l])
                    }
                    dob.tmp=para_hash
                  else p "#{__FILE__}:#{__LINE__}" if @md.opt.act[:maintenance][:set]==:on
                  end
                end
              end
            elsif (dob.tmp[:p] and dob.tmp[:l])
              dob.tmp = {
                p: number_paras_numbering(dob.tmp[:p]),
                l: number_paras_numbering(dob.tmp[:l])
              }
            else p "#{__FILE__}:#{__LINE__}" if @md.opt.act[:maintenance][:set]==:on
            end
          else
            dob=if dob.of !=:comment \
            || dob.of !=:meta \
            || dob.of !=:layout
              number_paras_numbering(dob)
            else dob
            end
          end
        end
        data
      end
      def output_morph_hash(o)
        ps,h,fn=o[:ps],o[:h],o[:filename]
        if h[ps] \
        and (h[ps][:p] or h[ps][:l])
          if @md.opt.act[:pdf_p][:set]==:on
            if h[ps][:p]
              h[ps][:p]=h[ps][:p].gsub(/[ ]+$/m,'').
                gsub(/\n\n\n+/m,"\n\n")
            end
            if h[ps][:p] !~/\A\s*\Z/
              fn[:portrait].puts h[ps][:p],"\n"
            end
          end
          if @md.opt.act[:pdf_l][:set]==:on
            if h[ps][:l]
              h[ps][:l]=h[ps][:l].gsub(/[ ]+$/m,'').
                gsub(/\n\n\n+/m,"\n\n")
            end
            if h[ps][:l] !~/\A\s*\Z/
              fn[:landscape].puts h[ps][:l],"\n"
            end
          end
        elsif (h[:p] or h[:l])
          if @md.opt.act[:pdf_p][:set]==:on
            if h[:p]
              h[:p]=h[:p].gsub(/[ ]+$/m,'').
                gsub(/\n\n\n+/m,"\n\n")
            end
            if h[:p] !~/\A\s*\Z/
              fn[:portrait].puts h[:p],"\n"
            end
          end
          if @md.opt.act[:pdf_l][:set]==:on
            if h[:l]
              h[:l]=h[:l].gsub(/[ ]+$/m,'').
                gsub(/\n\n\n+/m,"\n\n")
            end
            if h[:l] !~/\A\s*\Z/
              fn[:landscape].puts h[:l],"\n"
            end
          end
        else p "#{__FILE__}:#{__LINE__}" if @md.opt.act[:maintenance][:set]==:on
        end
      end
      def output(array)
        @array=array=array.flatten.compact
        fns=@md.fns.gsub(/~/,'-') #this is a sorry fix, but necessary as it appears latex programs like not ~
        @md.papersize_array.each do |ps|
          texfile_landscape=(@md.opt.act[:pdf_l][:set]==:on) \
          ? (File.new("#{@env.processing_path.tex}/#{fns}.#{ps}.landscape.tex",'w+'))
          : nil
          texfile_portrait=(@md.opt.act[:pdf_p][:set]==:on) \
          ? (File.new("#{@env.processing_path.tex}/#{fns}.#{ps}.tex",'w+'))
          : nil
          file={
            landscape: texfile_landscape,
            portrait:  texfile_portrait
          }
          if @md.opt.act[:pdf_p][:set]==:on
            file[:portrait] << @@tex_head[ps][:p]
          end
          if @md.opt.act[:pdf_l][:set]==:on
            file[:landscape] << @@tex_head[ps][:l]
          end
          array.each do |morph|
            if morph.is_a?(String)
              #morph.gsub!(/^\s+/,'')
              if morph !~/\A\s*\Z/
                if @md.opt.act[:pdf_p][:set]==:on
                  file[:portrait].puts morph,"\n"
                end
                if @md.opt.act[:pdf_l][:set]==:on
                  file[:landscape].puts morph,"\n"
                end
              end
            elsif morph.class.inspect =~ /SiSU_AO_DocumentStructure/ \
            and morph.tmp \
            and morph.tmp.is_a?(String)
              if morph.is !=:code \
              && morph.of !=:block
                morph.tmp=morph.tmp.gsub(/^\s+/,'')
              else morph.tmp
              end
              if (morph.tmp !~/\A\s*\Z/) \
              || morph.is==:code
                if @md.opt.act[:pdf_p][:set]==:on
                  file[:portrait].puts morph.tmp,"\n"
                end
                if @md.opt.act[:pdf_l][:set]==:on
                  file[:landscape].puts morph.tmp,"\n"
                end
              end
            elsif morph.is_a?(Hash)            #inserted headers and the like, only
              h={ ps: ps, h: morph, filename: file }
              output_morph_hash(h)
            elsif morph.tmp.is_a?(Hash)       #tables & images?
              h={ ps: ps, h: morph.tmp, filename: file }
              output_morph_hash(h)
            end
          end
          array=@array
          if @md.opt.act[:pdf_p][:set]==:on
            file[:portrait].close
          end
          if @md.opt.act[:pdf_l][:set]==:on
            file[:landscape].close
          end
        end
        @@tex_head={
          'a4'=>    { p: nil, l: nil },
          'a5'=>    { p: nil, l: nil },
          'b5'=>    { p: nil, l: nil },
          'letter'=>{ p: nil, l: nil },
          'legal'=> { p: nil, l: nil },
          'book'=>  { p: nil, l: nil }
        }
        array=[]
      end
    end
  end
end
__END__
