#! /usr/bin/env python
# style_showcase.py
# -*- encoding: utf-8 -*-
"""Make an illustration of a LaTeX style on a sample document.
"""
__version__='1.0.0'
__author__='Jim Hefferon ftpmaint at tug.ctan.org'
__date__='2010-Feb-16'
__notes__="""
"""

import sys, os, os.path, re, optparse
import time, subprocess

DEFAULT_PLAIN_TEXT_FN='plain.text'
DEFAULT_MATH_TEXT_FN='math.text'

DEFAULT_TEMP_DIR='tmp'
DEFAULT_OUTPUT_FN_FORMAT='style_showcase.tex'
DEFAULT_OUTPUT_FN=DEFAULT_OUTPUT_FN_FORMAT  # ?could want % (0,)

DEBUG=False
FORGIVE=False
VERBOSE=False

DEFAULT_DOCUMENTCLASS='article'
DEFAULT_SIZE='10pt'
USEPACKAGE_LIST=None
MATHPACKAGE_LIST=None
EXTRA_LIST=None

DEFAULT_PLAIN_TEXT=r"""
\usepackage{lipsum}
\title{Cartesian closed categories and the price of eggs}
\author{Jane Doe}
\date{September 1994}

\lipsum[1-2]

\section{First section}
\lipsum[3-4]

\subsection{First subsection of first section}
\lipsum[5-7]

\subsection{Second subsection of first section}
\lipsum[8-9]


\section{Second section}
\lipsum[11]

\subsection{First subsection of second section}
\lipsum[12]
"""

DEFAULT_MATH_TEXT=r"""\begin{thm}
Quisque aliquam $x$ ipsum sed turpis.
Pellentesque $y\in K$ laoreet velit nec justo.
Nam sed augue.
Maecenas rutrum quam eu dolor.
\begin{equation} 
  \int_5^6 x^2\,dx=A_{xt}
\end{equation}
Fusce consectetuer.
Proin tellus est, luctus vitae, molestie a, mattis et, mauris.
\begin{equation}\begin{split}
  H_c&=\frac{1}{2n} \sum^n_{l=0}(-1)^{l}(n-{l})^{p-2}
  \sum_{l _1+\dots+ l _p=l}\prod^p_{i=1} \binom{n_i}{l _i}\\
  &\quad\cdot[(n-l )-(n_i-l _i)]^{n_i-l _i}\cdot
  \Bigl[(n-l )^2-\sum^p_{j=1}(n_i-l _i)^2\Bigr].
\end{split}\end{equation}
Donec tempor. 
Pellentesque habitant morbi tristique senectus et netus et malesuada 
fames ac turpis egestas.
\end{thm}

\begin{proof}
Fusce adipiscing justo nec ante. 
Nullam in enim.
\begin{equation*}
  \left.\begin{aligned}
  B'&=-\partial\times E,\\
  E'&=\partial\times B - 4\pi j
  \end{aligned}
  \right\}
  \qquad \text{Maxwell's equations}
\end{equation*}
Pellentesque felis orci, sagittis ac, malesuada et, facilisis in, ligula. 
Nunc non magna sit amet mi aliquam dictum. 
\begin{equation}
  \frac{1}{k}\log_2 c(f)\quad\tfrac{1}{k}\log_2 c(f)\quad
  \sqrt{\frac{1}{k}\log_2 c(f)}\quad\sqrt{\dfrac{1}{k}\log_2 c(f)}
\end{equation}
In mi.
\end{proof}

\lipsum[26]

\begin{defn}
Aenean adipiscing auctor est. Morbi quam
arcu, malesuada sed, volutpat et, elementum sit amet, libero. Duis
accumsan. Curabitur urna.
\begin{equation}
  \begin{pmatrix} a&b&c&d\\
  e&\hdotsfor{3} \end{pmatrix}
\end{equation}
In sed ipsum.
\end{defn}

\begin{lem}
Donec lobortis nibh. 
Duis $x\in K_2$ mattis. 
Sed cursus lectus quis odio. 
Phasellus arcu. 
Praesent imperdiet dui in sapien. 
\end{lem}

\begin{proof}
Vestibulum tellus pede, auctor a, pellentesque sit amet, vulputate sed, purus. 
\begin{align}
  A_1&=N_0(\lambda;\Omega')-\phi(\lambda;\Omega'),\\
  A_2&=\phi(\lambda;\Omega')-\phi(\lambda;\Omega),\\
  \intertext{and}
  A_3&=\mathcal{N}(\lambda;\omega).
\end{align}
Nunc pulvinar, dui at eleifend adipiscing, tellus nulla placerat massa, 
sed condimentum nulla tellus sed ligula. 
Nulla vitae odio sit amet leo imperdiet blandit. 
In vel massa.
\begin{equation*}
  \sum_{\begin{subarray}{l}
        i\in\Lambda\\ 0<j<n
      \end{subarray}}
   P(i,j)
\end{equation*}
Maecenas varius dui at turpis. 
Sed odio.
\end{proof}

\begin{thm}
Sed justo. 
Maecenas lacinia, turpis sed commodo congue, odio urna elementum nunc, 
vitae molestie velit nunc eu sem. 
Maecenas enim.
\begin{equation}
  \displaystyle
  \sum_{\substack{0\le i\le m\\ 0<j<n}} P(i,j)
\end{equation}
Proin quis neque nec tortor sollicitudin volutpat.
\end{thm}

\begin{proof} 
Sed at ante. 
Sed vitae mauris non ante egestas hendrerit.
Cum sociis natoque penatibus et magnis dis parturient montes, nascetur 
ridiculus mus. 
\begin{equation}\label{xx}
  \begin{split}
  a& =b+c-d\\
   & \quad +e-f\\
   & =g+h\\
   & =i
  \end{split}
\end{equation}
\end{proof}

\begin{cor}
In venenatis $2\leq y,z\leq 5$ facilisis magna.
\begin{math}
  \bigl( \begin{smallmatrix}
    a&b\\ c&d
  \end{smallmatrix} \bigr)
\end{math}
Cras quis mauris. 
Aliquam eget magna. 
Donec rutrum sagittis mi. 
Morbi elementum, est sit amet sollicitudin feugiat, orci magna semper risus, 
eu congue nulla metus vel elit. 
\end{cor}
"""

def write_to_fh(fh,
                documentclass=None,
                size=None,
                usepackage_list=None,
                mathpackage_list=None,
                extra_list=None,
                plain_text=None,
                math_text=None):
    """Put the text to the file object.
    """
    if documentclass is None:
        documentclass='article'
    if size is None:
        size='10pt'
    top=r"""\documentclass[%s]{%s}
%% This document generated by style_showcase.py by Jim Hefferon, on %s
\usepackage{lipsum}
""" % (size,documentclass,time.asctime())
    fh.write(top)
    if usepackage_list is None:
        usepackage_list=[]
    for package in usepackage_list:
        package_line="""
\usepackage{%s}
""" % (package,)
        fh.write(package_line)
    if not(mathpackage_list is None):
        for package in mathpackage_list:
            if package:
                package_line="""
\usepackage{%s}
""" % (package,)
            fh.write(package_line)
    else:
        fh.write(r"\usepackage{amsmath,amsthm}")
        fh.write(r"""\theoremstyle{plain}
\newtheorem{thm}{Theorem}[section]
\newtheorem{lem}[thm]{Lemma}
\newtheorem{cor}[thm]{Corollary}
\theoremstyle{definition}
\newtheorem{defn}[thm]{Definition}
""")
    if extra_list is None:
        extra_list=[]
    for extra in extra_list:
        extra_line=r"""
%s
""" % (extra,)
        fh.write(extra_line)
    if plain_text is None:
        plain_text=DEFAULT_PLAIN_TEXT
    if math_text is None:
        math_text=DEFAULT_MATH_TEXT
    # Put in the text body
    fh.write(r"""\title{Cartesian closed categories and the price of eggs}
\author{Jane Doe}
\date{September 1994}

\begin{document}
\maketitle
""")
    fh.write(plain_text)
    fh.write(math_text)
    fh.write(r"\end{document}")
    
def write_to_file(fn,
                  temp_dir,
                  documentclass=None,
                  size=None,
                  usepackage_list=None,
                  mathpackage_list=None,
                  extra_list=None,
                  plain_text=None,
                  math_text=None):
    """Write the text to the given file name, in the directory temp_dir.
    """
    fh=open(temp_dir+os.sep+fn,'w')
    write_to_fh(fh,
                documentclass=documentclass,
                size=size,
                usepackage_list=usepackage_list,
                mathpackage_list=mathpackage_list,
                extra_list=extra_list,
                plain_text=plain_text,
                math_text=math_text)
    fh.close()


def syscall(s):
    """Run the system call
    s  string
    """
    mesg='while running '+repr(s)
    retcode = subprocess.check_call(s,shell=True)
    if retcode < 0:
        raise Exception, mesg+': unexpected return code '+repr(retcode)

def compile_file(fn,temp_dir):
    cwd=os.getcwd()
    os.chdir(temp_dir)
    syscall('pdflatex '+fn)
    syscall('pdflatex '+fn)
    os.chdir(cwd)

def convert_to_png(fn,temp_dir):
    """Convert the .pdf file to two flavors of .png: one like fn_small.png.0
    and the other without the '_small'.
    """
    fn=fn[:-len('.tex')]
    cwd=os.getcwd()
    os.chdir(temp_dir)
    try: # extra .png's from prior runs can fool you
        syscall("rm *.png.*")
    except: # no such files found
        pass
    syscall('convert -geometry 20% '+fn+'.pdf '+fn+'.png')
    for x in range(0,10):
        try:
            syscall('mv '+fn+'.png.'+str(x)+' '+fn+'_small.png.'+str(x))
        except:
            break
    syscall('convert -contrast '+fn+'.pdf '+fn+'.png')
    os.chdir(cwd)

def cleanup(fn,temp_dir):
    """Erase .log files, etc.
    """
    fn=fn[:-len('.tex')]
    cwd=os.getcwd()
    os.chdir(temp_dir)
    for ext in ['.aux','.log','.aux']:
        try:
            syscall('rm '+fn+ext)
        except:
            pass
    os.chdir(cwd)
    
def generate_sample(fn,
                    temp_dir,
                    documentclass=None,
                    size=None,
                    usepackage_list=None,
                    mathpackage_list=None,
                    extra_list=None,
                    plain_text=None,
                    math_text=None):
    """Generate the sample page.
      fn  string  Name of the .tex file
      temp_dir  string  Subdir where the output goes
      documentclass=None string  LaTeX document class
      size=None  string  Point size of main body text
      usepackage_list=None  list of strings  The packages that will be used in
        the LaTeX document
      mathpackage_list=None  list of strings  The math packages used.
      extra_list=None  list of strings  Extra lines to enter in the .tex
        file.
      plain_text=None  string  Used as the body of the document
      math_text=None  string  Used as the body of the document containing math
    """
    if not(os.path.isdir(temp_dir)):
        os.mkdir(temp_dir)
    write_to_file(fn,
                  temp_dir,
                  documentclass=documentclass,
                  size=size,
                  usepackage_list=usepackage_list,
                  mathpackage_list=mathpackage_list,
                  extra_list=extra_list,
                  plain_text=plain_text,
                  math_text=math_text)
    compile_file(fn,temp_dir)
    convert_to_png(fn,temp_dir)
    cleanup(fn,temp_dir)



#......................................................................
def main(argv=None):
    """The main logic if called from the command line
      argv=None  The arguments to the routine  
    """
    # Set the defaults
    if argv is None:
        argv=sys.argv
    # Parse the arguments
    #   First, define all options
    usage="""Illustrate an article style by LaTeX-ing a test document
  %prog  [options] <optional plain text filename> <optional math text filename>
The <filename>s can contain text to set as part of the document."""
    oP=optparse.OptionParser(usage=usage,version=__version__)
    oP.add_option('--output_file','-o',action='store',default=DEFAULT_OUTPUT_FN,dest='output_file',help='Output filename (default: %s)' % (DEFAULT_OUTPUT_FN,))
    oP.add_option('--temp_dir','-t',action='store',default=DEFAULT_TEMP_DIR,dest='temp_dir',help='Directory for output (default: %s)' % (DEFAULT_TEMP_DIR,))
    oP.add_option('--documentclass','-d',action='store',default=DEFAULT_DOCUMENTCLASS,dest='documentclass',help='LaTeX document class (default: %s)' % (DEFAULT_DOCUMENTCLASS,))
    oP.add_option('--size','-s',action='store',default=DEFAULT_SIZE,dest='size',help='Point size of main text (default: %s)' % (DEFAULT_SIZE,))
    oP.add_option('--usepackage','-u',action='append',default=USEPACKAGE_LIST,dest='usepackage_list',help='Use the LaTeX package (default: %s)' % (USEPACKAGE_LIST,))
    oP.add_option('--mathpackage','-m',action='append',default=MATHPACKAGE_LIST,dest='mathpackage_list',help='Use the LaTeX math package (default: %s)' % (MATHPACKAGE_LIST,))
    oP.add_option('--extra','-e',action='append',default=EXTRA_LIST,dest='extra_list',help=' (default: %s)' % (MATHPACKAGE_LIST,))
#     oP.add_option('--DEBUG','-D',action='store_true',default=DEBUG,dest='debug',help='output debugging information (default %s)' % (DEBUG,))
#     oP.add_option('--FORGIVE','-F',action='store_true',default=FORGIVE,dest='forgive',help='continue on failure (default: %s)' % (repr(FORGIVE),))
#     oP.add_option('--VERBOSE','-V',action='store_true',default=VERBOSE,dest='verbose',help='talk a lot (default %s)' % (VERBOSE,))
    opts, args=oP.parse_args(argv[1:])
    # Establish the infrastructure
    # Handle positional arguments
    try:
        plain_text_fn=args[0]
    except:
        plain_text_fn=DEFAULT_PLAIN_TEXT_FN
    plain_text=open(plain_text_fn,'r').read()    
    try:
        math_text_fn=args[1]
    except:
        math_text_fn=DEFAULT_MATH_TEXT_FN
    math_text=open(math_text_fn,'r').read()    
    # Handle the options
    generate_sample(opts.output_file,
                    opts.temp_dir,
                    documentclass=opts.documentclass,
                    size=opts.size,
                    usepackage_list=opts.usepackage_list,
                    mathpackage_list=opts.mathpackage_list,
                    extra_list=opts.extra_list,
                    plain_text=plain_text,
                    math_text=math_text)
    

if __name__=='__main__':
    if __notes__.strip():
        util.stdout("Notes for "+__file__+":\n"+__notes__)        
    try:
        main(argv=sys.argv)
    except KeyboardInterrupt:
        mesg=sys.argv[0]+u": Keyboard interrupt"
        print >>2, mesg
    sys.exit(0)
