mirror of
https://github.com/kennethreitz/context.git
synced 2026-06-05 23:00:17 +00:00
697 lines
15 KiB
Plaintext
697 lines
15 KiB
Plaintext
/*
|
|
* @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))
|
|
}
|