ref: 0ec1cfd78b495e9be40e4aa24e35b2aa5d5e5704
dir: /appl/charon/paginate.b/
implement Paginate; include "common.m"; include "print.m"; include "paginate.m"; sys: Sys; print: Print; L: Layout; D: Draw; Frame, Lay, Line, Control: import Layout; Docinfo: import Build; Image, Display, Rect, Point: import D; Item: import Build; MaskedImage: import CharonUtils; disp: ref Display; p0: Point; nulimg: ref Image; DPI: con 110; init(layout: Layout, draw: Draw, display: ref Draw->Display): string { sys = load Sys Sys->PATH; L = layout; D = draw; disp = display; if (L == nil || D == nil || disp == nil) return "bad args"; print = load Print Print->PATH; if (print == nil) return sys->sprint("cannot load module %s: %r", Print->PATH); print->init(); #nullfd := sys->open("/dev/null", Sys->OWRITE); #print->set_printfd(nullfd); p0 = Point(0, 0); nulimg = disp.newimage(((0, 0), (1, 1)), 3, 0, 0); return nil; } paginate(frame: ref Layout->Frame, orient: int, pnums, cancel: chan of int, result: chan of (string, ref Pageset)) { pidc := chan of int; spawn watchdog(pidc, cancel, nil, sys->pctl(0, nil)); watchpid := <- pidc; if (frame.kids != nil) { result <-= ("cannot print frameset", nil); kill(watchpid); return; } defp := print->get_defprinter(); if (defp == nil) { result <-= ("no default printer", nil); kill(watchpid); return; } # assuming printer's X & Y resolution are the same if (orient == PORTRAIT) defp.popt.orientation = Print->PORTRAIT; else defp.popt.orientation = Print->LANDSCAPE; (dpi, pagew, pageh) := print->get_size(defp); pagew = (DPI * pagew)/dpi; pageh = (DPI * pageh)/dpi; pfr := copyframe(frame); pr := Rect(p0, (pagew, pageh)); pfr.r = pr; pfr.cr = pr; pfr.viewr = pr; l := pfr.layout; L->relayout(pfr, l, pagew, l.just); maxy := l.height + l.margin; # don't include bottom margin prctxt := ref Layout->Printcontext; pfr.prctxt = prctxt; pfr.cim = nulimg; pnum := 1; startys : list of int; for (y := 0; y < maxy;) { startys = y :: startys; pnums <-= pnum++; endy := y + pageh; prctxt.endy = pageh; pfr.viewr.min.y = y; pfr.viewr.max.y = endy; L->drawall(pfr); y += prctxt.endy; } # startys are in reverse order ys : list of int; for (; startys != nil; startys = tl startys) ys = hd startys :: ys; pageset := ref Pageset(defp, pfr, ys); result <-= (nil, pageset); kill(watchpid); } printpageset(pset: ref Pageset, pnums, cancel: chan of int) { pidc := chan of int; stopdog := chan of int; spawn watchdog(pidc, cancel, stopdog, sys->pctl(0, nil)); watchpid := <- pidc; frame := pset.frame; pageh := frame.cr.dy(); white := disp.rgb2cmap(255, 255, 255); prctxt := frame.prctxt; l := frame.layout; maxy := l.height + l.margin; # don't include bottom margin maxy = max(maxy, pageh); pnum := 1; for (pages := pset.pages; pages != nil; pages = tl pages) { y := hd pages; if (y + pageh > maxy) pageh = maxy - y; frame.cr.max.y = pageh; frame.cim = disp.newimage(frame.cr, 3, 0, white); if (frame.cim == nil) { pnums <-= -1; kill(watchpid); return; } pnums <-= pnum++; endy := y + pageh; prctxt.endy = pageh; frame.viewr.min.y = y; frame.viewr.max.y = endy; L->drawall(frame); stopdog <-= 1; #start := sys->millisec(); if (print->print_image(pset.printer, disp, frame.cim, 100, cancel) == -1) { # cancelled kill(watchpid); return; } stopdog <-= 1; #sys->print("PAGE %d: %dms\n", pnum -1, sys->millisec()-start); } pnums <-= -1; kill(watchpid); } watchdog(pidc, cancel, pause: chan of int, pid: int) { pidc <-= sys->pctl(0, nil); if (pause == nil) pause = chan of int; for (;;) alt { <- cancel => kill(pid); return; <- pause => <- pause; } } kill(pid: int) { sys->fprint(sys->open("/prog/" + string pid +"/ctl", Sys->OWRITE), "kill"); } killgrp(pid: int) { sys->fprint(sys->open("/prog/" + string pid +"/ctl", Sys->OWRITE), "killgrp"); } max(a, b: int): int { if (a > b) return a; return b; } copyframe(f: ref Frame): ref Frame { zr := Draw->Rect(p0, p0); newf := ref Frame( -1, # id nil, # doc nil, # src " PRINT FRAME ", # name f.marginw, # marginw f.marginh, # marginh 0, # framebd Build->FRnoscroll, # flags nil, # layout - filled in below, needs this frame ref nil, # sublays - filled in by geometry code 0, # sublayid nil, # controls - filled in below, needs this frame ref 0, # controlid - filled in below nil, # cim zr, # r zr, # cr zr, # totalr zr, # viewr nil, # vscr nil, # hscr nil, # parent nil, # kids 0, # animpid nil # prctxt ); newf.doc = copydoc(f, newf, f.doc); controls := array [len f.controls] of ref Control; for (i := 0; i < len controls; i++) controls[i] = copycontrol(f, newf, f.controls[i]); newf.layout = copylay(f, newf, f.layout); newf.controls = controls; newf.controlid = len controls; return newf; } copysublay(oldf, f: ref Frame, oldid:int): int { if (oldid < 0) return -1; if (f.sublayid >= len f.sublays) f.sublays = (array [len f.sublays + 30] of ref Lay)[:] = f.sublays; id := f.sublayid++; lay := copylay(oldf, f, oldf.sublays[oldid]); f.sublays[id] = lay; return id; } copydoc(oldf, f : ref Frame, doc: ref Build->Docinfo): ref Docinfo { background := copybackground(oldf, f, doc.background); newdoc := ref Build->Docinfo( nil, #src nil, #base nil, #referrer nil, #doctitle background, nil, #backgrounditem doc.text, doc.link, doc.vlink, doc.alink, nil, #target nil, #refresh nil, #chset nil, #lastModified 0, #scripttype 0, #hasscripts nil, #events 0, #evmask nil, #kidinfo 0, #frameid nil, #anchors nil, #dests nil, #forms nil, #tables nil, #maps nil #images ); return newdoc; } copylay(oldf, f: ref Frame, l: ref Lay): ref Lay { start := copyline(oldf, f, nil, l.start); end := start; for (line := l.start.next; line != nil; line = line.next) end = copyline(oldf, f, end, line); newl := ref Lay( start, end, l.targetwidth, # targetwidth l.width, # width l.height, # height l.margin, # margin nil, # floats - filled in by geometry code copybackground(oldf, f, l.background), l.just, Layout->Lchanged ); start.flags = end.flags = byte 0; return newl; } copycontrol(oldf, f: ref Frame, ctl: ref Control): ref Control { if (ctl == nil) return nil; pick c := ctl { Cbutton => return ref Control.Cbutton(f, nil, c.r, c.flags, nil, c.pic, c.picmask, c.dpic, c.dpicmask, c.label, c.dorelief); Centry => scr := copycontrol(oldf, f, c.scr); return ref Control.Centry(f, nil, c.r, c.flags, nil, scr, c.s, c.sel, c.left, c.linewrap, 0); Ccheckbox=> return ref Control.Ccheckbox(f, nil, c.r, c.flags, nil, c.isradio); Cselect => scr := copycontrol(oldf, f, c.scr); options := (array [len c.options] of Build->Option)[:] = c.options; return ref Control.Cselect(f, nil, c.r, c.flags, nil, nil, scr, c.nvis, c.first, options); Clistbox => hscr := copycontrol(oldf, f, c.hscr); vscr := copycontrol(oldf, f, c.vscr); options := (array [len c.options] of Build->Option)[:] = c.options; return ref Control.Clistbox(f, nil, c.r, c.flags, nil, hscr, vscr, c.nvis, c.first, c.start, c.maxcol, options, nil); Cscrollbar => # do not copy ctl as this is set by those associated controls return ref Control.Cscrollbar(f, nil, c.r, c.flags, nil, c.top, c.bot, c.mindelta, c.deltaval, nil, c.holdstate); Canimimage => bg := copybackground(oldf, f, c.bg); return ref Control.Canimimage(f, nil, c.r, c.flags, nil, c.cim, 0, 0, big 0, bg); Clabel => return ref Control.Clabel(f, nil, c.r, c.flags, nil, c.s); * => return nil; } } copyline(oldf, f: ref Frame, prev, l: ref Line): ref Line { if (l == nil) return nil; cp := ref *l; items := copyitems(oldf, f, l.items); newl := ref Line (items, nil, prev, l.pos, l.width, l.height, l.ascent, Layout->Lchanged); if (prev != nil) prev.next = newl; return newl; } copyitems(oldf, f: ref Frame, items: ref Item): ref Item { if (items == nil) return nil; item := copyitem(oldf, f, items); end := item; for (items = items.next; items != nil; items = items.next) { end.next = copyitem(oldf, f, items); end = end.next; } return item; } copyitem(oldf, f : ref Frame, item: ref Item): ref Item { if (item == nil) return nil; pick it := item { Itext => return ref Item.Itext( nil, it.width, it.height, it.ascent, 0, it.state, nil, it.s, it.fnt, it.fg, it.voff, it.ul); Irule => return ref Item.Irule( nil, it.width, it.height, it.ascent, 0, it.state, nil, it.align, it.noshade, it.size, it.wspec); Iimage => # need to copy the image to prevent # ongoing image fetches from messing up our layout ci := copycimage(it.ci); return ref Item.Iimage( nil, it.width, it.height, it.ascent, 0, it.state, nil, it.imageid, ci, it.imwidth, it.imheight, it.altrep, nil, it.name, -1, it.align, it.hspace, it.vspace, it.border); Iformfield => return ref Item.Iformfield( nil, it.width, it.height, it.ascent, 0, it.state, nil, copyformfield(oldf, f, it.formfield) ); Itable => return ref Item.Itable( nil, it.width, it.height, it.ascent, 0, it.state, nil, copytable(oldf, f, it.table)); Ifloat => items := copyitem(oldf, f, it.item); return ref Item.Ifloat( nil, it.width, it.height, it.ascent, 0, it.state, nil, items, it.x, it.y, it.side, byte 0); Ispacer => return ref Item.Ispacer( nil, it.width, it.height, it.ascent, 0, it.state, nil, it.spkind, it.fnt); * => return nil; } } copycimage(ci: ref CharonUtils->CImage): ref CharonUtils->CImage { if (ci == nil) return nil; mims : array of ref MaskedImage; if (len ci.mims > 0) # if len> 1 then animated, but we only want first frame mims = array [1] of {0 => ci.mims[0]}; return ref CharonUtils->CImage(nil, nil, nil, 0, ci.width, ci.height, nil, mims, 0); } copyformfield(oldf, f: ref Frame, ff: ref Build->Formfield): ref Build->Formfield { image := copyitem(oldf, f, ff.image); # should be safe to reference Option list newff := ref Build->Formfield( ff.ftype, 0, nil, ff.name, ff.value, ff.size, ff.maxlength, ff.rows, ff.cols, ff.flags, ff.options, image, ff.ctlid, nil, 0 ); return newff; } copytable(oldf, f: ref Frame, tbl: ref Build->Table): ref Build->Table { nrow := tbl.nrow; ncol := tbl.ncol; caption_lay := copysublay(oldf, f, tbl.caption_lay); cols := (array [ncol] of Build->Tablecol)[:] = tbl.cols; rows := array [nrow] of ref Build->Tablerow; for (i := 0; i < nrow; i++) { r := tbl.rows[i]; rows[i] = ref Build->Tablerow(nil, r.height, r.ascent, r.align, r.background, r.pos, r.flags); } cells : list of ref Build->Tablecell; grid := array [nrow] of {* => array [ncol] of ref Build->Tablecell}; for (rix := 0; rix < nrow; rix++) { rowcells: list of ref Build->Tablecell = nil; for (colix := 0; colix < ncol; colix++) { cell := copytablecell(oldf, f, tbl.grid[rix][colix]); if (cell == nil) continue; grid[rix][colix] = cell; cells = cell :: cells; rowcells = cell :: rowcells; } # reverse the row cells; rcells : list of ref Build->Tablecell = nil; for (; rowcells != nil; rowcells = tl rowcells) rcells = hd rowcells :: rcells; rows[rix].cells = rcells; } # reverse the cells sllec: list of ref Build->Tablecell; for (; cells != nil; cells = tl cells) sllec = hd cells :: sllec; cells = sllec; return ref Build->Table( tbl.tableid, # tableid nrow, # nrow ncol, # ncol len cells, # ncell tbl.align, # align tbl.width, # width tbl.border, # border tbl.cellspacing, # cellspacing tbl.cellpadding, # cellpadding tbl.background, # background nil, # caption tbl.caption_place, # caption_place caption_lay, # caption_lay nil, # currows cols, # cols rows, # rows cells, # cells tbl.totw, # totw tbl.toth, # toth tbl.caph, # caph tbl.availw, # availw grid, # grid nil, # tabletok Layout->Lchanged # flags ); } copytablecell(oldf, f: ref Frame, cell: ref Build->Tablecell): ref Build->Tablecell { if (cell == nil) return nil; layid := copysublay(oldf, f, cell.layid); background := copybackground(oldf, f, cell.background); newcell := ref Build->Tablecell( cell.cellid, nil, # content layid, cell.rowspan, cell.colspan, cell.align, cell.flags, cell.wspec, cell.hspec, background, cell.minw, cell.maxw, cell.ascent, cell.row, cell.col, cell.pos); return newcell; } copybackground(oldf, f: ref Frame, bg: Build->Background): Build->Background { img := copyitem(oldf, f, bg.image); if (img != nil) { pick i := img { Iimage => bg.image = i; } } return bg; }