From 37da2899f40661e3e9631e497da8dc59b971cbd0 Mon Sep 17 00:00:00 2001 From: "Charles.Forsyth" Date: Fri, 22 Dec 2006 17:07:39 +0000 Subject: 20060303a --- appl/cmd/cook.b | 1924 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1924 insertions(+) create mode 100644 appl/cmd/cook.b (limited to 'appl/cmd/cook.b') diff --git a/appl/cmd/cook.b b/appl/cmd/cook.b new file mode 100644 index 00000000..0d333a4d --- /dev/null +++ b/appl/cmd/cook.b @@ -0,0 +1,1924 @@ +implement Cook; + +include "sys.m"; + sys: Sys; + FD: import Sys; + +include "draw.m"; + draw: Draw; + +include "bufio.m"; + B: Bufio; + Iobuf: import B; + +include "string.m"; + S: String; + splitl, splitr, splitstrl, drop, take, in, prefix, tolower : import S; + +include "brutus.m"; + Size6, Size8, Size10, Size12, Size16, NSIZE, + Roman, Italic, Bold, Type, NFONT, NFONTTAG, + Example, Caption, List, Listelem, Label, Labelref, + Exercise, Heading, Nofill, Author, Title, + Index, Indextopic, + DefFont, DefSize, TitleFont, TitleSize, HeadingFont, HeadingSize: import Brutus; + +# following are needed for types in brutusext.m +include "tk.m"; + tk: Tk; +include "tkclient.m"; + +include "brutusext.m"; + SGML, Text, Par, Extension, Float, Special, Celem, + FLatex, FLatexProc, FLatexBook, FLatexPart, FLatexSlides, FHtml: import Brutusext; + +include "strinttab.m"; + T: StringIntTab; + +Cook: module +{ + init: fn(ctxt: ref Draw->Context, args: list of string); +}; + +# keep this sorted by name +tagstringtab := array[] of { T->StringInt + ("Author", Author), + ("Bold.10", Bold*NSIZE + Size10), + ("Bold.12", Bold*NSIZE + Size12), + ("Bold.16", Bold*NSIZE + Size16), + ("Bold.6", Bold*NSIZE + Size6), + ("Bold.8", Bold*NSIZE + Size8), + ("Caption", Caption), + ("Example", Example), + ("Exercise", Exercise), + ("Extension", Extension), + ("Float", Float), + ("Heading", Heading), + ("Index", Index), + ("Index-topic", Indextopic), + ("Italic.10", Italic*NSIZE + Size10), + ("Italic.12", Italic*NSIZE + Size12), + ("Italic.16", Italic*NSIZE + Size16), + ("Italic.6", Italic*NSIZE + Size6), + ("Italic.8", Italic*NSIZE + Size8), + ("Label", Label), + ("Label-ref", Labelref), + ("List", List), + ("List-elem", Listelem), + ("No-fill", Nofill), + ("Par", Par), + ("Roman.10", Roman*NSIZE + Size10), + ("Roman.12", Roman*NSIZE + Size12), + ("Roman.16", Roman*NSIZE + Size16), + ("Roman.6", Roman*NSIZE + Size6), + ("Roman.8", Roman*NSIZE + Size8), + ("SGML", SGML), + ("Title", Title), + ("Type.10", Type*NSIZE + Size10), + ("Type.12", Type*NSIZE + Size12), + ("Type.16", Type*NSIZE + Size16), + ("Type.6", Type*NSIZE + Size6), + ("Type.8", Type*NSIZE + Size8), +}; + +# This table must be sorted +fmtstringtab := array[] of { T->StringInt + ("html", FHtml), + ("latex", FLatex), + ("latexbook", FLatexBook), + ("latexpart", FLatexPart), + ("latexproc", FLatexProc), + ("latexslides", FLatexSlides), +}; + +Transtab: adt +{ + ch: int; + trans: string; +}; + +# Order doesn't matter for these table + +ltranstab := array[] of { Transtab + ('$', "\\textdollar{}"), + ('&', "\\&"), + ('%', "\\%"), + ('#', "\\#"), + ('_', "\\textunderscore{}"), + ('{', "\\{"), + ('}', "\\}"), + ('~', "\\textasciitilde{}"), + ('^', "\\textasciicircum{}"), + ('\\', "\\textbackslash{}"), + ('+', "\\textplus{}"), + ('=', "\\textequals{}"), + ('|', "\\textbar{}"), + ('<', "\\textless{}"), + ('>', "\\textgreater{}"), + (' ', "~"), + ('-', "-"), # needs special case ligature treatment + ('\t', " "), # needs special case treatment +}; + +htranstab := array[] of { Transtab + ('α', "α"), + ('Æ', "Æ"), + ('Á', "Á"), + ('Â', "Â"), + ('À', "À"), + ('Å', "Å"), + ('Ã', "Ã"), + ('Ä', "Ä"), + ('Ç', "Ç"), + ('Ð', "Ð"), + ('É', "É"), + ('Ê', "Ê"), + ('È', "È"), + ('Ë', "Ë"), + ('Í', "Í"), + ('Î', "Î"), + ('Ì', "Ì"), + ('Ï', "Ï"), + ('Ñ', "Ñ"), + ('Ó', "Ó"), + ('Ô', "Ô"), + ('Ò', "Ò"), + ('Ø', "Ø"), + ('Õ', "Õ"), + ('Ö', "Ö"), + ('Þ', "Þ"), + ('Ú', "Ú"), + ('Û', "Û"), + ('Ù', "Ù"), + ('Ü', "Ü"), + ('Ý', "Ý"), + ('æ', "&aElig;"), + ('á', "á"), + ('â', "â"), + ('à', "à"), + ('α', "α"), + ('&', "&"), + ('å', "å"), + ('ã', "ã"), + ('ä', "ä"), + ('β', "β"), + ('ç', "ç"), + ('⋯', "&cdots;"), + ('χ', "χ"), + ('©', "©"), + ('⋱', "&ddots;"), + ('δ', "δ"), + ('é', "é"), + ('ê', "ê"), + ('è', "è"), + ('—', "&emdash;"), + (' ', " "), + ('–', "&endash;"), + ('ε', "ε"), + ('η', "η"), + ('ð', "ð"), + ('ë', "ë"), + ('γ', "γ"), + ('>', ">"), + ('í', "í"), + ('î', "î"), + ('ì', "ì"), + ('ι', "ι"), + ('ï', "ï"), + ('κ', "κ"), + ('λ', "λ"), + ('…', "&ldots;"), + ('<', "<"), + ('μ', "μ"), + (' ', " "), + ('ñ', "ñ"), + ('ν', "ν"), + ('ó', "ó"), + ('ô', "ô"), + ('ò', "ò"), + ('ω', "ω"), + ('ο', "ο"), + ('ø', "ø"), + ('õ', "õ"), + ('ö', "ö"), + ('φ', "φ"), + ('π', "π"), + ('ψ', "ψ"), + (' ', "&quad;"), + ('"', """), + ('®', "®"), + ('ρ', "ρ"), + ('­', "­"), + ('σ', "σ"), + ('ß', "ß"), + ('τ', "τ"), + ('θ', "θ"), + (' ', " "), + ('þ', "þ"), + ('™', "™"), + ('ú', "ú"), + ('û', "û"), + ('ù', "ù"), + ('υ', "υ"), + ('ü', "ü"), + ('∈', "ϵ"), + ('ϕ', "ϕ"), + ('ϖ', "ϖ"), + ('ϱ', "ϱ"), + ('⋮', "&vdots;"), + ('ς', "&vsigma;"), + ('ϑ', "&vtheta;"), + ('ξ', "ξ"), + ('ý', "ý"), + ('ÿ', "ÿ"), + ('ζ', "ζ"), + ('−', "-"), +}; + +# For speedy lookups of ascii char translation, use asciitrans. +# It should be initialized by ascii elements from one of above tables +asciitrans := array[128] of string; + +stderr: ref FD; +infilename := ""; +outfilename := ""; +linenum := 0; +fin : ref Iobuf = nil; +fout : ref Iobuf = nil; +debug := 0; +fmt := FLatex; + +init(nil: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + S = load String String->PATH; + B = load Bufio Bufio->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + T = load StringIntTab StringIntTab->PATH; + stderr = sys->fildes(2); + + for(argv = tl argv; argv != nil; ) { + s := hd argv; + tlargv := tl argv; + case s { + "-f" => + if(tlargv == nil) + usage(); + fnd: int; + (fnd, fmt) = T->lookup(fmtstringtab, hd(tlargv)); + if(!fnd) { + sys->fprint(stderr, "unknown format: %s\n", hd(tlargv)); + exit; + } + argv = tlargv; + "-o" => + if(tlargv == nil) + usage(); + outfilename = hd(tlargv); + argv = tlargv; + "-d" => + debug = 1; + "-dd" => + debug = 2; + * => + if(tlargv == nil) + infilename = s; + else + usage(); + } + argv = tl argv; + } + if(infilename == "") { + fin = B->fopen(sys->fildes(0), sys->OREAD); + infilename = ""; + } + else + fin = B->open(infilename, sys->OREAD); + if(fin == nil) { + sys->fprint(stderr, "cook: error opening %s: %r\n", infilename); + exit; + } + if(outfilename == "") { + fout = B->fopen(sys->fildes(1), sys->OWRITE); + outfilename = ""; + } + else + fout = B->create(outfilename, sys->OWRITE, 8r664); + if(fout == nil) { + sys->fprint(stderr, "cook: error creating %s: %r\n", outfilename); + exit; + } + line0 := fin.gets('\n'); + if(line0 != "\n") { + parse_err("not an SGML file\n"); + exit; + } + linenum = 1; + e := parse(SGML); + findpars(e, 1, nil); + e = delemptystrs(e); + (e, nil) = canonfonts(e, DefFont*NSIZE+DefSize, DefFont*NSIZE+DefSize); + mergeadjs(e); + findfloats(e); + cleanexts(e); + cleanpars(e); + if(debug) { + fout.puts("After Initial transformations:\n"); + printelem(e, "", 1); + fout.flush(); + } + case fmt { + FLatex or FLatexProc or FLatexBook or FLatexPart or FLatexSlides => + latexconv(e); + FHtml => + htmlconv(e); + } + fin.close(); + fout.close(); +} + +usage() +{ + sys->fprint(stderr, "Usage: cook [-f (latex|html)] [-o outfile] [infile]\n"); + exit; +} + +parse_err(msg: string) +{ + sys->fprint(stderr, "%s:%d: %s\n", infilename, linenum, msg); +} + +# Parse into elements. +# Assumes tags are balanced. +# String elements are split so that there is never an internal newline. +parse(id: int) : ref Celem +{ + els : ref Celem = nil; + elstail : ref Celem = nil; + for(;;) { + c := fin.getc(); + if(c == Bufio->EOF) { + if(id == SGML) + break; + else { + parse_err(sys->sprint("EOF while parsing %s", tagname(id))); + return nil; + } + } + if(c == '<') { + tag := ""; + start := 1; + i := 0; + for(;;) { + c = fin.getc(); + if(c == Bufio->EOF) { + parse_err("EOF in middle of tag"); + return nil; + } + if(c == '\n') { + linenum++; + parse_err("newline in middle of tag"); + break; + } + if(c == '>') + break; + if(i == 0 && c == '/') + start = 0; + else + tag[i++] = c; + } + (fnd, tid) := T->lookup(tagstringtab, tag); + if(!fnd) { + if(prefix("Extension ", tag)) { + el := ref Celem(Extension, tag[10:], nil, nil, nil, nil); + if(els == nil) { + els = el; + elstail = el; + } + else { + el.prev = elstail; + elstail.next = el; + elstail = el; + } + } + else + parse_err(sys->sprint("unknown tag <%s>\n", tag)); + continue; + } + if(start) { + el := parse(tid); + if(el == nil) + return nil; + if(els == nil) { + els = el; + elstail = el; + } + else { + el.prev = elstail; + elstail.next = el; + elstail = el; + } + } + else { + if(tid != id) { + parse_err(sys->sprint("<%s> ended by ", + tagname(id), tag)); + continue; + } + break; + } + } + else { + s := ""; + i := 0; + for(;;) { + if(c == Bufio->EOF) + break; + if(c == '<') { + fin.ungetc(); + break; + } + if(c == ';' && i >=3 && s[i-1] == 't' && s[i-2] == 'l' && s[i-3] == '&') { + i -= 2; + s[i-1] = '<'; + s = s[0:i]; + } + else + s[i++] = c; + if(c == '\n') { + linenum++; + break; + } + else + c = fin.getc(); + } + if(s != "") { + el := ref Celem(Text, s, nil, nil, nil, nil); + if(els == nil) { + els = el; + elstail = el; + } + else { + el.prev = elstail; + elstail.next = el; + elstail = el; + } + } + } + } + ans := ref Celem(id, "", els, nil, nil, nil); + if(els != nil) + els.parent = ans; + return ans; +} + +# Modify tree e so that blank lines become Par elements. +# Only do it if parize is set, and unset parize when descending into TExample's. +# Pass in most recent TString or TPar element, and return updated most-recent-TString/TPar. +# This function may set some TString strings to "" +findpars(e: ref Celem, parize: int, prevspe: ref Celem) : ref Celem +{ + while(e != nil) { + prevnl := 0; + prevpar := 0; + if(prevspe != nil) { + if(prevspe.tag == Text && len prevspe.s != 0 + && prevspe.s[(len prevspe.s)-1] == '\n') + prevnl = 1; + else if(prevspe.tag == Par) + prevpar = 1; + } + if(e.tag == Text) { + if(parize && (prevnl || prevpar) && e.s[0] == '\n') { + if(prevnl) + prevspe.s = prevspe.s[0 : (len prevspe.s)-1]; + e.tag = Par; + e.s = nil; + } + prevspe = e; + } + else { + nparize := parize; + if(e.tag == Example) + nparize = 0; + prevspe = findpars(e.contents, nparize, prevspe); + } + e = e.next; + } + return prevspe; +} + +# Delete any empty strings from e's tree and return modified e. +# Also, delete any entity that has empty contents, except the +# Par ones +delemptystrs(e: ref Celem) : ref Celem +{ + if(e.tag == Text) { + if(e.s == "") + return nil; + else + return e; + } + if(e.tag == Par || e.tag == Extension || e.tag == Special) + return e; + h := e.contents; + while(h != nil) { + hnext := h.next; + hh := delemptystrs(h); + if(hh == nil) + delete(h); + h = hnext; + } + if(e.contents == nil) + return nil; + return e; +} + +# Change tree under e so that any font elems contain only strings +# (by pushing the font changes down). +# Answer an be a list, so return beginning and end of list. +# Leave strings bare if font change would be to deffont, +# and adjust deffont appropriately when entering Title and +# Heading environments. +canonfonts(e: ref Celem, curfont, deffont: int) : (ref Celem, ref Celem) +{ + f := curfont; + head : ref Celem = nil; + tail : ref Celem = nil; + tocombine : ref Celem = nil; + if(e.tag == Text) { + if(f == deffont) { + head = e; + tail = e; + } + else { + head = ref Celem(f, nil, e, nil, nil, nil); + e.parent = head; + tail = head; + } + } + else if(e.contents == nil) { + head = e; + tail = e; + } + else if(e.tag < NFONTTAG) { + f = e.tag; + allstrings := 1; + for(g := e.contents; g != nil; g = g.next) { + if(g.tag != Text) + allstrings = 0; + tail = g; + } + if(allstrings) { + if(f == deffont) + head = e.contents; + else { + head = e; + tail = e; + } + } + } + if(head == nil) { + if(e.tag == Title) + deffont = TitleFont*NSIZE+TitleSize; + else if(e.tag == Heading) + deffont = HeadingFont*NSIZE+HeadingSize; + for(h := e.contents; h != nil; ) { + prev := h.prev; + next := h.next; + excise(h); + (e1, en) := canonfonts(h, f, deffont); + splicebetween(e1, en, prev, next); + if(prev == nil) + head = e1; + tail = en; + h = next; + } + tocombine = head; + if(e.tag >= NFONTTAG) { + e.contents = head; + head.parent = e; + head = e; + tail = e; + } + } + if(tocombine != nil) { + # combine adjacent font changes to same font + r := tocombine; + while(r != nil) { + if(r.tag < NFONTTAG && r.next != nil && r.next.tag == r.tag) { + for(v := r.next; v != nil; v = v.next) { + if(v.tag != r.tag) + break; + if(v == tail) + tail = r; + } + # now r up to, not including v, all change to same font + for(p := r.next; p != v; p = p.next) { + append(r.contents, p.contents); + } + r.next = v; + if(v != nil) + v.prev = r; + r = v; + } + else + r = r.next; + } + } + head.parent = nil; + return (head, tail); +} + +# Remove Pars that appear just before or just after Heading, Title, Examples, Extensions +# Really should worry about this happening at different nesting levels, but in +# practice this happens all at the same nesting level +cleanpars(e: ref Celem) +{ + for(h := e.contents; h != nil; h = h.next) { + cleanpars(h); + if(h.tag == Title || h.tag == Heading || h.tag == Example || h.tag == Extension) { + hp := h.prev; + hn := h.next; + if(hp !=nil && hp.tag == Par) + delete(hp); + if(hn != nil && hn.tag == Par) + delete(hn); + } + } +} + +# Remove a single tab if it appears before an Extension +cleanexts(e: ref Celem) +{ + for(h := e.contents; h != nil; h = h.next) { + cleanexts(h); + if(h.tag == Extension) { + hp := h.prev; + if(hp != nil && stringof(hp) == "\t") + delete(hp); + } + } +} + +mergeable := array[] of { List, Exercise, Caption,Index, Indextopic }; + +# Merge some adjacent elements (which were probably created separate +# because of font changes) +mergeadjs(e: ref Celem) +{ + for(h := e.contents; h != nil; h = h.next) { + hn := h.next; + domerge := 0; + if(hn != nil) { + for(i := 0; i < len mergeable; i++) { + mi := mergeable[i]; + if(h.tag == mi && hn.tag == mi) + domerge = 1; + } + } + if(domerge) { + append(h.contents, hn.contents); + delete(hn); + } + else + mergeadjs(h); + } +} + +# Find floats: they are paragraphs with Captions at the end. +findfloats(e: ref Celem) +{ + lastpar : ref Celem = nil; + for(h := e.contents; h != nil; h = h.next) { + if(h.tag == Par) + lastpar = h; + else if(h.tag == Caption) { + ne := ref Celem(Float, "", nil, nil, nil, nil); + if(lastpar == nil) + flhead := e.contents; + else + flhead = lastpar.next; + insertbefore(ne, flhead); + # now move flhead ... h into contents of ne + ne.contents = flhead; + flhead.parent = ne; + flhead.prev = nil; + ne.next = h.next; + if(ne.next != nil) + ne.next.prev = ne; + h.next = nil; + h = ne; + } + else + findfloats(h); + } +} + +insertbefore(e, ebefore: ref Celem) +{ + e.prev = ebefore.prev; + if(e.prev == nil) { + e.parent = ebefore.parent; + ebefore.parent = nil; + e.parent.contents = e; + } + else + e.prev.next = e; + e.next = ebefore; + ebefore.prev = e; +} + +insertafter(e, eafter: ref Celem) +{ + e.next = eafter.next; + if(e.next != nil) + e.next.prev = e; + e.prev = eafter; + eafter.next = e; +} + +# remove e from its list, leaving siblings disconnected +excise(e: ref Celem) +{ + next := e. next; + prev := e.prev; + e.next = nil; + e.prev = nil; + if(prev != nil) + prev.next = nil; + if(next != nil) + next.prev = nil; + e.parent = nil; +} + +splicebetween(e1, en, prev, next: ref Celem) +{ + if(prev != nil) + prev.next = e1; + e1.prev = prev; + en.next = next; + if(next != nil) + next.prev = en; +} + +append(e1, e2: ref Celem) +{ + e1last := last(e1); + e1last.next = e2; + e2.prev = e1last; + e2.parent = nil; +} + +last(e: ref Celem) : ref Celem +{ + if(e != nil) + while(e.next != nil) + e = e.next; + return e; +} + +succ(e: ref Celem) : ref Celem +{ + if(e == nil) + return nil; + if(e.next != nil) + return e.next; + return succ(e.parent); +} + +delete(e: ref Celem) +{ + ep := e.prev; + en := e.next; + eu := e.parent; + if(ep == nil) { + if(eu != nil) + eu.contents = en; + if(en != nil) + en.parent = eu; + } + else + ep.next = en; + if(en != nil) + en.prev = ep; +} + +# return string represented by e, peering through font changes +stringof(e: ref Celem) : string +{ + if(e != nil) { + if(e.tag == Text) + return e.s; + if(e.tag < NFONTTAG) + return stringof(e.contents); + } + return ""; +} + +# remove any initial whitespace from e and its sucessors, +dropwhite(e: ref Celem) +{ + if(e == nil) + return; + del := 0; + if(e.tag == Text) { + e.s = drop(e.s, " \t\n"); + if(e.s == "") + del = 1;; + } + else if(e.tag < NFONTTAG) { + dropwhite(e.contents); + if(e.contents == nil) + del = 1; + } + if(del) { + enext := e.next; + delete(e); + dropwhite(enext); + } + +} + +firstchar(e: ref Celem) : int +{ + s := stringof(e); + if(len s >= 1) + return s[0]; + return -1; +} + +lastchar(e: ref Celem) : int +{ + if(e == nil) + return -1; + while(e.next != nil) + e = e.next; + s := stringof(e); + if(len s >= 1) + return s[len s -1]; + return -1; +} + +tlookup(t: array of Transtab, v: int) : string +{ + n := len t; + for(i := 0; i < n; i++) + if(t[i].ch == v) + return t[i].trans; + return ""; +} + +initasciitrans(t: array of Transtab) +{ + n := len t; + for(i := 0; i < n; i++) { + c := t[i].ch; + if(c < 128) + asciitrans[c] = t[i].trans; + } +} + +tagname(id: int) : string +{ + name := T->revlookup(tagstringtab, id); + if(name == nil) + name = "_unknown_"; + return name; +} + +printelem(e: ref Celem, indent: string, recurse: int) +{ + fout.puts(indent); + if(debug > 1) { + fout.puts(sys->sprint("%x: ", e)); + if(e != nil && e.parent != nil) + fout.puts(sys->sprint("(parent %x): ", e.parent)); + } + if(e == nil) + fout.puts("NIL\n"); + else if(e.tag == Text || e.tag == Special || e.tag == Extension) { + if(e.tag == Special) + fout.puts("S"); + else if(e.tag == Extension) + fout.puts("E"); + fout.puts("«"); + fout.puts(e.s); + fout.puts("»\n"); + } + else { + name := tagname(e.tag); + fout.puts("<" + name + ">\n"); + if(recurse && e.contents != nil) + printelems(e.contents, indent + " ", recurse); + } +} + +printelems(els: ref Celem, indent: string, recurse: int) +{ + for(; els != nil; els = els.next) + printelem(els, indent, recurse); +} + +check(e: ref Celem, msg: string) +{ + err := checke(e); + if(err != "") { + fout.puts(msg + ": tree is inconsistent:\n" + err); + printelem(e, "", 1); + fout.flush(); + exit; + } +} + +checke(e: ref Celem) : string +{ + err := ""; + if(e.tag == SGML && e.next != nil) + err = sys->sprint("root %x has a next field\n", e); + ec := e.contents; + if(ec != nil) { + if(ec.parent != e) + err += sys->sprint("node %x contents %x has bad parent %x\n", e, ec, e.parent); + if(ec.prev != nil) + err += sys->sprint("node %x contents %x has non-nil prev %x\n", e, ec, e.prev); + p := ec; + for(h := ec.next; h != nil; h = h.next) { + if(h.prev != p) + err += sys->sprint("node %x comes after %x, but prev is %x\n", h, p, h.prev); + if(h.parent != nil) + err += sys->sprint("node %x, not first in siblings, has parent %x\n", h, h.parent); + p = h; + } + for(h = ec; h != nil; h = h.next) { + err2 := checke(h); + if(err2 != nil) + err += err2; + } + } + return err; +} + +# Translation to Latex + +# state bits +SLT, SLB, SLI, SLS6, SLS8, SLS12, SLS16, SLE, SLO, SLF : con (1< SLS6, + Roman*NSIZE+Size8 => SLS8, + Roman*NSIZE+Size10 => 0, + Roman*NSIZE+Size12 => SLS12, + Roman*NSIZE+Size16 => SLS16, + Italic*NSIZE+Size6 => SLI | SLS6, + Italic*NSIZE+Size8 => SLI | SLS8, + Italic*NSIZE+Size10 => SLI, + Italic*NSIZE+Size12 => SLI | SLS12, + Italic*NSIZE+Size16 => SLI | SLS16, + Bold*NSIZE+Size6 => SLB | SLS6, + Bold*NSIZE+Size8 => SLB | SLS8, + Bold*NSIZE+Size10 => SLB, + Bold*NSIZE+Size12 => SLB | SLS12, + Bold*NSIZE+Size16 => SLB | SLS16, + Type*NSIZE+Size6 => SLT | SLS6, + Type*NSIZE+Size8 => SLT | SLS8, + Type*NSIZE+Size10 => SLT, + Type*NSIZE+Size12 => SLT | SLS12, + Type*NSIZE+Size16 => SLT | SLS16 +}; + +lsizecmd := array[] of { "\\footnotesize", "\\small", "\\normalsize", "\\large", "\\Large"}; +llinepos : int; +lslidenum : int; +LTABSIZE : con 4; + +latexconv(e: ref Celem) +{ + initasciitrans(ltranstab); + + case fmt { + FLatex or FLatexProc => + if(fmt == FLatex) { + fout.puts("\\documentclass{article}\n"); + fout.puts("\\def\\encodingdefault{T1}\n"); + } + else { + fout.puts("\\documentclass[10pt,twocolumn]{article}\n"); + fout.puts("\\def\\encodingdefault{T1}\n"); + fout.puts("\\usepackage{latex8}\n"); + fout.puts("\\bibliographystyle{latex8}\n"); + } + fout.puts("\\usepackage{times}\n"); + fout.puts("\\usepackage{brutus}\n"); + fout.puts("\\usepackage{unicode}\n"); + fout.puts("\\usepackage{epsf}\n"); + title := lfindtitle(e); + authors := lfindauthors(e); + abstract := lfindabstract(e); + fout.puts("\\begin{document}\n"); + if(title != nil) { + fout.puts("\\title{"); + llinepos = 0; + lconvl(title, 0); + fout.puts("}\n"); + if(authors != nil) { + fout.puts("\\author{"); + for(l := authors; l != nil; l = tl l) { + llinepos = 0; + lconvl(hd l, SLO|SLI); + if(tl l != nil) + fout.puts("\n\\and\n"); + } + fout.puts("}\n"); + } + fout.puts("\\maketitle\n"); + } + fout.puts("\\pagestyle{empty}\\thispagestyle{empty}\n"); + if(abstract != nil) { + if(fmt == FLatexProc) { + fout.puts("\\begin{abstract}\n"); + llinepos = 0; + lconvl(abstract, 0); + fout.puts("\\end{abstract}\n"); + } + else { + fout.puts("\\section*{Abstract}\n"); + llinepos = 0; + lconvl(abstract, 0); + } + } + FLatexBook => + fout.puts("\\documentclass{ibook}\n"); + fout.puts("\\usepackage{brutus}\n"); + fout.puts("\\usepackage{epsf}\n"); + fout.puts("\\begin{document}\n"); + FLatexSlides => + fout.puts("\\documentclass[portrait]{seminar}\n"); + fout.puts("\\def\\encodingdefault{T1}\n"); + fout.puts("\\usepackage{times}\n"); + fout.puts("\\usepackage{brutus}\n"); + fout.puts("\\usepackage{unicode}\n"); + fout.puts("\\usepackage{epsf}\n"); + fout.puts("\\centerslidesfalse\n"); + fout.puts("\\slideframe{none}\n"); + fout.puts("\\slidestyle{empty}\n"); + fout.puts("\\pagestyle{empty}\n"); + fout.puts("\\begin{document}\n"); + lslidenum = 0; + } + + llinepos = 0; + if(e.tag == SGML) + lconvl(e.contents, 0); + + if(fmt == FLatexSlides && lslidenum > 0) + fout.puts("\\vfill\\end{slide*}\n"); + if(fmt != FLatexPart) + fout.puts("\\end{document}\n"); +} + +lconvl(el: ref Celem, state: int) +{ + for(e := el; e != nil; e = e.next) { + tag := e.tag; + op := ""; + cl := ""; + parlike := 1; + nstate := state; + if(tag < NFONTTAG) { + parlike = 0; + ss := lftagtostate[tag]; + if((state & SLFONTMASK) != ss) { + t := state & SLT; + b := state & SLB; + i := state & SLI; + newt := ss & SLT; + newb := ss & SLB; + newi := ss & SLI; + op = "{"; + cl = "}"; + if(t && !newt) + op += "\\rmfamily"; + else if(!t && newt) + op += "\\ttfamily"; + if(b && !newb) + op += "\\mdseries"; + else if(!b && newb) + op += "\\bfseries"; + if(i && !newi) + op += "\\upshape"; + else if(!i && newi) { + op += "\\itshape"; + bc := lastchar(e.contents); + ac := firstchar(e.next); + if(bc != -1 && bc != ' ' && bc != '\n' && ac != -1 && ac != '.' && ac != ',') + cl = "\\/}"; + } + if((state & SLSIZEMASK) != (ss & SLSIZEMASK)) { + nsize := 2; + if(ss & SLS6) + nsize = 0; + else if(ss & SLS8) + nsize = 1; + else if(ss & SLS12) + nsize = 3; + else if(ss & SLS16) + nsize = 4; + # examples shrunk one size + if((state & SLE) && nsize > 0) + nsize--; + op += lsizecmd[nsize]; + } + fc := firstchar(e.contents); + if(fc == ' ') + op += "{}"; + else + op += " "; + nstate = (state & ~SLFONTMASK) | ss; + } + } + else + case tag { + Text => + parlike = 0; + if(state & SLO) { + asciitrans[' '] = "\\ "; + asciitrans['\n'] = "\\\\\n"; + } + s := e.s; + n := len s; + for(k := 0; k < n; k++) { + c := s[k]; + x := ""; + if(c < 128) + x = asciitrans[c]; + else + x = tlookup(ltranstab, c); + if(x == "") { + fout.putc(c); + if(c == '\n') + llinepos = 0; + else + llinepos++; + } + else { + # split up ligatures + if(c == '-' && k < n-1 && s[k+1] == '-') + x = "-{}"; + # Avoid the 'no line to end here' latex error + if((state&SLO) && c == '\n' && llinepos == 0) + fout.puts("\\ "); + else if((state&SLO) && c == '\t') { + nspace := LTABSIZE - llinepos%LTABSIZE; + llinepos += nspace; + while(nspace-- > 0) + fout.puts("\\ "); + + } + else { + fout.puts(x); + if(x[len x - 1] == '\n') + llinepos = 0; + else + llinepos++; + } + } + } + if(state & SLO) { + asciitrans[' '] = nil; + asciitrans['\n'] = nil; + } + Example => + if(!(state&SLE)) { + op = "\\begin{example}"; + cl = "\\end{example}\\noindent "; + nstate |= SLE | SLO; + } + List => + (n, bigle) := lfindbigle(e.contents); + if(n <= 2) { + op = "\\begin{itemize}\n"; + cl = "\\end{itemize}"; + } + else { + fout.puts("\\begin{itemizew}{"); + lconvl(bigle.contents, nstate); + op = "}\n"; + cl = "\\end{itemizew}"; + } + Listelem => + op = "\\item[{"; + cl = "}]"; + Heading => + if(fmt == FLatexProc) + op = "\n\\Section{"; + else + op = "\n\\section{"; + cl = "}\n"; + nstate = (state & ~SLFONTMASK) | (SLB | SLS12); + Nofill => + op = "\\begin{nofill}"; + cl = "\\end{nofill}\\noindent "; + nstate |= SLO; + Title => + if(fmt == FLatexSlides) { + op = "\\begin{slide*}\n" + + "\\begin{center}\\Large\\bfseries "; + if(lslidenum > 0) + op = "\\vfill\\end{slide*}\n" + op; + cl = "\\end{center}\n"; + lslidenum++; + } + else { + if(stringof(e.contents) == "Index") { + op = "\\printindex\n"; + e.contents = nil; + } + else { + op = "\\chapter{"; + cl = "}\n"; + } + } + nstate = (state & ~SLFONTMASK) | (SLB | SLS16); + Par => + op = "\n\\par\n"; + while(e.next != nil && e.next.tag == Par) + e = e.next; + Extension => + e.contents = convextension(e.s); + if(e.contents != nil) + e.contents.parent = e; + Special => + fout.puts(e.s); + Float => + if(!(state&SLF)) { + isfig := lfixfloat(e); + if(isfig) { + op = "\\begin{figure}\\begin{center}\\leavevmode "; + cl = "\\end{center}\\end{figure}"; + } + else { + op = "\\begin{table}\\begin{center}\\leavevmode "; + cl = "\\end{center}\\end{table}"; + } + nstate |= SLF; + } + Caption=> + if(state&SLF) { + op = "\\caption{"; + cl = "}"; + nstate = (state & ~SLFONTMASK) | SLS8; + } + else { + op = "\\begin{center}"; + cl = "\\end{center}"; + } + Label or Labelref => + parlike = 0; + if(tag == Label) + op = "\\label"; + else + op = "\\ref"; + cl = "{" + stringof(e.contents) + "}"; + e.contents = nil; + Exercise => + lfixexercise(e); + op = "\\begin{exercise}"; + cl = "\\end{exercise}"; + Index or Indextopic => + parlike = 0; + if(tag == Index) + lconvl(e.contents, nstate); + fout.puts("\\showidx{"); + lconvl(e.contents, nstate); + fout.puts("}"); + lconvindex(e.contents, nstate); + e.contents = nil; + } + if(op != "") + fout.puts(op); + if(e.contents != nil) { + if(parlike) + llinepos = 0; + lconvl(e.contents, nstate); + if(parlike) + llinepos = 0; + } + if(cl != "") + fout.puts(cl); + } +} + +lfixfloat(e: ref Celem) : int +{ + dropwhite(e.contents); + fstart := e.contents; + fend := last(fstart); + hasfig := 0; + hastab := 0; + if(fend.tag == Caption) { + dropwhite(fend.prev); + if(fend.prev != nil && stringof(fstart) == "\t") + delete(fend.prev); + # If fend.contents is "YYY "