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 "wmlib.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 "