/*
 * @progname       rtflib.li
 * @version        1.1
 * @author         Doug McCallum
 * @category       
 * @output         RTF
 * @description    
 *
 * RTF functions for implementing RTF output
 *     this allows generating Word or other
 *     documents directly.
 *
 */
global(rtf_termstring)
global(rtf_tcols)
global(rtf_row_width)
global(rtf_row_left)
global(rtf_set_cols)
global(rtf_col_sizes)
global(rtf_pointsize)
global(rtf_pstate)
global(rtf_curr_indent)
global(rtf_tstate)
global(rtf_cstate)
global(rtf_ccol)
global(twips)          /* 20 points per inch */
global(rtf_bspace)
global(rtf_aspace)
global(rtf_ftn_last_tag)       /* last footnote tag */
global(rtf_ftn_state)

/*
 * Initialize the RTF state machine and variables
 */

/*
 * rtf_init(font)
 *     initialize the RTF state.
 *     set font as the default font to use (not working yet)
 */
proc rtf_init(font)
{
       set(twips, 1440)
       list(rtf_pointsize)
       setel(rtf_pointsize, 1, 24)     /* style 1 at 12pt */
       setel(rtf_pointsize, 2, 18)     /* style 2 at 9pt */
       setel(rtf_pointsize, 3, 20)     /* style 3 at 10pt */
       set(rtf_curr_indent, 0)         /* no paragraph indent */

       /* some table state */
       set(rtf_ccol, 0)
       set(rtf_pstate, 0)
       set(rtf_cstate, 0)
       list(rtf_col_sizes)
       set(rtf_set_cols, 0)

       /* all RTF files need this */
       "{\\rtf1\\ansi\\deff2{\\fonttbl{\\f10\\fnil "
       font
       ";}}\n"
       "{\\stylesheet{\\fs20\\basedon222\\snext0\\f10 Normal;}\n"
       "{\\s1\\fs24\\basedon0\\snext3\\f10\\b\\sb240\\sa60 Heading;}\n"
       "{\\s2\\basedon1\\fs20\\f10\\up Footnote;}\n"
       "{\\s3\\basedon222\\snext3\\f10\\fs20\\sb120\\sa10 Text;}\n"
       /* you can add new styles here */
       "}\n"
       set(rtf_termstring, "\n\\par}\n")
       monthformat(4)
       dayformat(0)
       dateformat(5)   /* nn-MON-yyyy */
}

/*
 * rtf_open(file)
 *     just do some setup.  If "file" defined, open it.
 */
proc rtf_open(file)
{
       if (file) {
               newfile(file, 0)
       }
       call rtf_init("Palatino")
}

/*
 * rtf_close()
 *     closes open tables and paragraphs then
 *     adds the closing bracket for the document
 */
proc rtf_close()
{
       call rtf_tend()
       call rtf_pend()
       rtf_termstring
}

/*
 * rtf_set_page_size(height, width, left, right, top, bot)
 *     set the page size if non-standard size is desired.
 *     height and width are paper size
 *     left, right, top and bot are the margin sizes
 *     sizes in twips (20pts/inch :: 1440 == 1inch)
 */
proc rtf_set_page_size(height, width, left, right, top, bot)
{
       "\\paperw" d(width)
       "\\paperh" d(height)
       "\\margl"  d(left)
       "\\margr"  d(right)
       "\\margt"  d(top)
       "\\margb"  d(bottom) nl()
}

/*
 * rtf_newpage()
 *     insert a forced pagebreak at this point
 */
proc rtf_newpage()
{
       "\\page "
}

/*
 * paragraph functions
 *     there are a number of options here
 */

/*
 * rtf_pstart(type)
 *     start a new paragraph with the style selected
 */
proc rtf_pstart(type)
{
       /* if in a paragraph, end it */
       if (eq(rtf_pstate, 1)) {
               call rtf_pend()
       }
       set(rtf_pstate, 1)
       "\\pard {\\s" d(type)
       if (ps, getel(rtf_pointsize, type)) {
               "\\fs" d(ps) " "
       }

       call rtf_para_space(rtf_bspace, rtf_aspace)

       if (gt(rtf_curr_indent, 0)) {
               /* "next" paragraph */
               call rtf_para_indent(0, rtf_curr_indent)
       }
}

/*
 * rtf_pend()
 *     end the current paragraph
 *     this is for completeness but a new pstart will do it
 *     so it is optional
 */
proc rtf_pend()
{
       if (eq(1, rtf_pstate)) {
               "\\par}\n"
               set(rtf_pstate, 0)
       }
}

/*
 * rtf_para_indent(first, all)
 *     tagged/indented paragraphs
 *     should be called right after a rtf_pstart()
 *     to select the indent of the first and remaining lines
 *     Note that the first line is also indented the same as
 *     all but can have more or less indent applied
 *     typical is to have first be the neg of the all
 *     a tab will make a hanging indent in that case
 */
proc rtf_para_indent(first, all)
{
       if (or(gt(first, 0), gt(all, 0))) {
               "\\li" d(all) "\\fi"
               d(first)
               "\\tx" d(all) " "
       }
       set(rtf_curr_indent, all)
}

/*
 * rtf_para_space(before, after)
 *     amount of white space before and after a paragraph
 */
proc rtf_para_space(before, after)
{
    if (rtf_pstate) {
       if (ne(before, 0)) {
               "\\sb" d(before)
       } else {
               "\\sb" d(mul(getel(rtf_pointsize, 3), 5))
       }
       if (ne(after, 0)) {
               "\\sa" d(after)
       }
    }
    set(rtf_bspace, before)
    set(rtf_aspace, after)
}

/*
 * rtf_para_keepnext()
 *     causes current paragraph to be kept on same page as
 *     the next paragraph
 */
proc rtf_para_keepnext()
{
       "\\keepn "
}

/*
 * rtf_para_centered()
 *     make the current paragraph text be centered
 */
proc rtf_para_centered()
{
       "\\qc "
}

/*
 * rtf_para_leftjust()
 *     make the current paragraph text left justified
 */
proc rtf_para_leftjust()
{
       "\\ql "
}

/*
 * rtf_para_rightjust()
 *     make the current paragraph text right justified
 */
proc rtf_para_rightjust()
{
       "\\qr "
}

/*
 * rtf_para_keepintact()
 *     don't try to break this paragraph across pages
 */
proc rtf_para_keepintact()
{
       "\\keep "
}

/*
 * rtf_set_info(title, subject, author, operator, created)
 *     set the file's info section to have the values specified
 */
proc rtf_set_info(title, subject, author, operator, created)
{
       "{\\info\n"
       if (title) {
               "{\\title " title "}\n"
       }
       if (subject) {
               "{\\subject " subject " }\n"
       }
       if (author) {
               "{\\author " author "}\n"
       }
       if (operator) {
               "{\\operator " operator "}\n"
       }
       if (created) {

               set(yr, save(substring(created, 1, 4)))
               set(mo, save(substring(created, 6, 7)))
               set(dy, save(substring(created, 9, 10)))
               "{\\creatim\\yr" yr
                            "\\mo" mo
                            "\\dy" dy "}\n"
       }
       "{\\doccomm Document generated from LifeLines "
               version()
               " database "
               database() "by register-rtf 1.1.}\n"
       "}\n"
}

/*
 * table functions
 *     there are a number related to rows and cells
 */

/*
 * rtf_set_row_width(cols, wid)
 *     set the table row width and number of columns to expect
 */
proc rtf_set_row_width(cols, wid)
{
       set(rtf_tcols, cols)
       set(rtf_row_width, sub(wid, mul(sub(rtf_tcols, 1), 108)))
       set(rtf_row_left, rtf_row_width)
}

/*
 * rtf_set_col_width(wid)
 *     set the current column width
 *     called once for each column defined
 */
proc rtf_set_col_width(wid)
{
    if (lt(rtf_set_cols, rtf_tcols)) {
       setel(rtf_col_sizes, one(rtf_set_cols), wid)
       incr(rtf_set_cols)
       set(rtf_row_left, sub(rtf_row_left, wid))
       set(i, rtf_set_cols)
       while (lt(i, rtf_tcols)) {
               setel(rtf_col_sizes, one(i),
                       div(rtf_row_left, sub(rtf_tcols, rtf_set_cols)))
               incr(i)
       }
    }
}

/*
 * rtf_tstart(cells)
 *     start table with cells per row
 */
proc rtf_tstart(cells)
{
       if (eq(rtf_tstate, 1)) {
               call rtf_tend()
       }
       call rtf_pend()
       "\\trowd "
       set(rtf_tstate, 1)
       "\\trgaph" d(108)
       "\\trleft" d(neg(108))
       set(i, 0)
       set(cumwid, 0)
       while (lt(i, cells)) {
               if (gt(i, 0)) {
                       set(gap, 108)
               } else {
                       set(gap, 0)
               }
               set(gap, add(gap, getel(rtf_col_sizes, one(i))))
               set(cumwid, add(cumwid, gap))
               "\\cellx" d(cumwid) "\n"
               set(i, add(i, 1))
       }
       set(rtf_tcols, cells)
       "\\pard\\plain\\s3\\intbl "
}

/*
 * rtf_tend()
 *     end table
 */
proc rtf_tend()
{
       if (rtf_tstate) {
               while (lt(rtf_ccol, rtf_tcols)) {
                       rtf_cend()
               }
               "\\intbl\\row\\pard\\s3 "
               set(rtf_tstate, 0)
       }
}

/*
 * rtf_cstart()
 *     start a cell in a table
 */
proc rtf_cstart()
{
       if (rtf_cstate) {
               call rtf_cend()
       }
       call rtf_pend()
       "\\fs" d(getel(rtf_pointsize, 3))
       set(rtf_cstate, 1)
}

/*
 * rtf_cend()
 *     end a cell
 */
proc rtf_cend()
{
       if (or(rtf_cstate, rtf_tstate)) {
               set(rtf_cstate, 0)
               "\n\\cell "
               set(rtf_ccol, add(rtf_ccol, 1))
       }
}

/*
 * rtf_cpar()
 *     insert a paragraph break inside a cell
 */
proc rtf_cpar()
{
       "\\par "
}

/*
 * rtf_endrow()
 *     end a table row and get ready for next one
 */
proc rtf_endrow()
{
       if (rtf_tstate) {
               while (lt(rtf_ccol, rtf_tcols)) {
                       call rtf_cend()
               }
               "\\pard\\s3\\inttbl\\row "
               set(rtf_tstate, 0)
               call rtf_tstart(rtf_tcols)
               set(rtf_ccol, 0)
       }
}

/* heading handling */

/*
 * rtf_hstart()
 *     start a heading
 */
proc rtf_hstart()
{
       "\\sb" d(mul(getel(rtf_pointsize, 1), 12))
       "\\sa" d(mul(getel(rtf_pointsize, 1), 6)) " "
       "{\\tc\\s1\\b "
}

/*
 * rtf_hend()
 *     end a heading
 */
proc rtf_hend()
{
       "\\b0}\n"
}

/*
 * rtf_index(key, subkey, type)
 *     create an index entry
 *     if subkey is defined, a two level index is
 *     created.  e.g.
 *     McCallum
 *             Charles         1
 *     the type is plain = 0, bold = 1 and italic = 2
 */
func rtf_index(key, subkey, type)
{
       if (eq(type, 0)) {
               set(var, "}}\n")
       } elsif (eq(type, 1)) {
               set(var, "\\bxe}}\n")
       } elsif (eq(type, 2)) {
               set(var, "\\ixe}}\n")
       }
       set(ind, concat("{\\xe{\\v ", key))
       if (subkey) {
               set(inds, concat("\\:", subkey))
       } else {
               set(inds, "")
       }
       set(indy, concat(ind, inds, var))
       return (indy)
}

/*
 * rtf_header(type, page)
 *     create a header entry (as in header/footer)
 *     type is all pages = 0, left = 1 and right = 2
 *     page is where to place the page number
 *     no page number = 0, left side = 1, center = 2 and right = 3
 */
proc rtf_header(type, page)
{
       if (eq(page, 0)) {
               set(pstr, "")
               set(pastr, "")
       } elsif (eq(page, 1)) {
               set(pastr, "\\ql")
               set(pstr, "\\chpgn")
       } elsif (eq(page, 2)) {
               set(pastr, "\\qc")
               set(pstr, "\\chpgn")
       } elsif (eq(page, 3)) {
               set(pastr, "\\qr")
               set(pstr, "\\chpgn")
       }
       if (eq(type, 0)) {
               set(hstr, "\\header")
       } elsif (eq(type, 1)) {
               set(hstr, "\\headerl")
       } elsif (eq(type, 2)) {
               set(hstr, "\\headerr")
       }
       "{" hstr "\\pard\\plain\\s3" pastr "{\\plain " pstr "}\\par}\n"
}
/*
 * rtf_footer(type, page)
 *     creates a footer.
 *     see rtf_header for details
 */
proc rtf_footer(type, page)
{
       if (eq(page, 0)) {
               set(pstr, "")
               set(pastr, "")
       } elsif (eq(page, 1)) {
               set(pastr, "\\ql")
               set(pstr, "\\chpgn")
       } elsif (eq(page, 2)) {
               set(pastr, "\\qc")
               set(pstr, "\\chpgn")
       } elsif (eq(page, 3)) {
               set(pastr, "\\qr")
               set(pstr, "\\chpgn")
       }
       if (eq(type, 0)) {
               set(hstr, "\\footer")
       } elsif (eq(type, 1)) {
               set(hstr, "\\footerl")
       } elsif (eq(type, 2)) {
               set(hstr, "\\footerr")
       }
       "{" hstr "\\pard\\plain\\s3" pastr "{\\plain " pstr "}\\par}\n"
}

/*
 * rtf_ftn_type(type, postype)
 *     define the type(s) of footnotes/endnotes to use
 */
proc rtf_ftn_type(type, postype)
{
       "\\fet" d(type)
       if (eq(type, 1)) {
               if (eq(postype, 0)) {
                       "\\enddoc\\aenddoc"
               } elsif (eq(postype, 1)) {
                       "\\endnotes\\aendnotes"
               }
       } elsif (eq(type, 2)) {
               if (eq(postype, 0)) {
                       "\\aenddoc"
               } elsif (eq(postype, 1)) {
                       "\\aendnotes"
               }
       }
       "\n"
}

/*
 * rtf_ftn_tag(tag)
 *     if tag is not null, it is a user defined tag
 *     if null, do an automatic generation of the tag
 *     In all cases, output it superscripted
 */
proc rtf_ftn_tag(tag)
{
       if (tag) {
               set(rtf_ftn_last_tag, tag)
       } else {
               set(rtf_ftn_last_tag, "\\chftn")
       }
       "{\\up6 " rtf_ftn_last_tag "}"
}

/*
 * rtf_ftn_start(tag)
 *     start a possibly tagged footnote
 *     must be closed with rtf_ftn_end()
 */
proc rtf_ftn_start(tag)
{
       if (rtf_ftn_state) {
               call rtf_ftn_end()
       }
       call rtf_ftn_tag(tag)
       "{\*\footnote\\pard\\plain\\s3\\fs"
       d(getel(rtf_pointsize, 3))
       "\\li-540\\fi540\\tx540 "
       rtf_ftn_last_tag
       "\tab "
       set(rtf_ftn_state, 1)
}
/*
 * rtf_ftn_end()
 *     close an open footnote.
 */
proc rtf_ftn_end()
{
       if (rtf_ftn_state) {
               "}\n"
               set(rtf_ftn_state, 0)
       }
}

/*
 * rtf_tab(type)
 *     issue a tab of appropriate type
 */
proc rtf_tab(type)
{
       if (eq(type, 0)) {
               "\\tab "
       } elsif (eq(type, 1)) {
               "\\tqr "
       } elsif (eq(type, 2)) {
               "\\tqc "
       }
}

/*
 * rtf_bold(on)
 *     turn bold on/off
 */
proc rtf_bold(on)
{
       if (on) {
               "\\b "
       } else {
               "\\b0 "
       }
}

/*
 * rtf_italic(on)
 *     turn italic on/off
 */
proc rtf_italic(on)
{
       if (on) {
               "\\i "
       } else {
               "\\i0 "
       }
}

/*
 * rtf_underline(type)
 *     turn underline on/off
 *     if type == 0 off
 *     1 == continuous, 2 == double, 3 == word, 4 == dotted
 */
proc rtf_underline(type)
{
       if (type) {
               if (eq(type, 1)) { "\\ul " }
               elsif (eq(type, 2)) { "\\uldb " }
               elsif (eq(type, 3)) { "\\ulw " }
               elsif (eq(type, 4)) { "\\uld " }
       } else {
               "\\ul0 "
       }
}

/*
 * rtf_super(on)
 *     turn superscript on/off
 */
proc rtf_super(on)
{
       if (on) {
               "{\\up6"
               if (ps, getel(rtf_pointsize, 3)) {
                       set(ps, sub(ps, 3))
                       "\\fs" d(ps)
               }
               " "
       } else {
               "}"
       }
}

/*
 * rtf_toc_entry(level, text)
 *     enter text as a Table of Contents entry at level
 */
proc rtf_toc_entry(level, text)
{
       "{\\tc\\tcl" d(level)
       "{\\v " text "}}"
}

/*
 * one(val)
 *     similar to incr() but returns the new value
 */
func one(val)
{
       return (add(val, 1))
}
