1implement Print; 2 3include "sys.m"; 4 sys: Sys; 5include "draw.m"; 6 draw: Draw; 7 Display, Font, Rect, Point, Image, Screen: import draw; 8include "bufio.m"; 9 bufio: Bufio; 10include "string.m"; 11 str: String; 12 13include "print.m"; 14 15MAXNAME: con 80; 16DEFMODE: con 8r664; 17 18PAPER_CONFIG: con CONFIG_PATH + "paper.cfg"; 19PTYPE_CONFIG: con CONFIG_PATH + "ptype.cfg"; 20PMODE_CONFIG: con CONFIG_PATH + "pmode.cfg"; 21POPT_CONFIG: con CONFIG_PATH + "popt.cfg"; 22PRINTER_CONFIG: con CONFIG_PATH + "printer.cfg"; 23DEFPRINTER: con CONFIG_PATH + "defprinter"; 24 25 26Cfg: adt { 27 name: string; 28 pairs: list of (string, string); 29}; 30 31DEBUG :=0; 32 33 34all_papers: list of ref Paper; 35all_pmodes: list of ref Pmode; 36all_ptypes: list of ref Ptype; 37all_popts: list of ref Popt; 38all_printers: list of ref Printer; 39default_printer: ref Printer; 40stderr: ref Sys->FD; 41printfd: ref Sys->FD; 42 43# Initialization 44 45init(): int 46{ 47 sys = load Sys Sys->PATH; 48 stderr = sys->fildes(2); 49 draw = load Draw Draw->PATH; 50 bufio = load Bufio Bufio->PATH; 51 str = load String String->PATH; 52 all_papers = read_paper_config(); 53 if (all_papers == nil) return 1; 54 all_pmodes = read_pmode_config(); 55 if (all_pmodes == nil) return 1; 56 all_ptypes = read_ptype_config(); 57 if (all_ptypes == nil) return 1; 58 all_printers = read_printer_config(); 59 if (all_printers == nil) return 1; 60 all_popts = read_popt_config(); 61 for (pl:=all_printers; pl!=nil; pl=tl pl) { 62 p := hd pl; 63 opt := find_popt(all_popts, p.name); 64 if (opt != nil) p.popt = opt; 65 else { 66 p.popt = ref Popt (p.name, hd all_pmodes, hd all_papers, 0, 0); 67 all_popts = p.popt :: all_popts; 68 } 69 } 70 return 0; 71} 72 73# Set printer FD 74 75set_printfd(fd: ref Sys->FD) 76{ 77 printfd = fd; 78} 79 80 81# Get default printer 82 83get_defprinter(): ref Printer 84{ 85 if (len all_printers == 1) return hd all_printers; # If there's only 1 printer 86 df := sys->open(DEFPRINTER, Sys->OREAD); 87 if (df == nil) { 88 if (all_printers != nil) return hd all_printers; 89 else return nil; 90 } 91 a := array[MAXNAME] of byte; 92 nb := sys->read(df, a, MAXNAME); 93 if (nb < 2) return nil; 94 name := string a[:nb-1]; 95 def := find_printer(all_printers, name); 96 if (def != nil) return def; 97 else return hd all_printers; 98} 99 100# Set default printer 101 102set_defprinter(p: ref Printer) 103{ 104 df := sys->create(DEFPRINTER, Sys->OWRITE, DEFMODE); 105 if (df == nil) return; 106 sys->fprint(df, "%s\n", p.name); 107} 108 109# Set paper size 110 111get_size(p: ref Printer): (int, int, int) # dpi, xpixels, ypixels 112{ 113 if (p == nil) return (0, 0, 0); 114 load_driver(p); 115 dpi := p.popt.mode.resx; 116 (xpix, ypix) := p.pdriver->printable_pixels(p); # This takes account of orientation 117 return (dpi, xpix, ypix); 118} 119 120 121 122# Get list of all printers 123 124get_printers(): list of ref Printer 125{ 126 return all_printers; 127} 128 129# Return list of printer types 130 131get_ptypes(): list of ref Ptype 132{ 133 return all_ptypes; 134} 135 136# Return list of print modes 137 138get_pmodes(): list of ref Pmode 139{ 140 return all_pmodes; 141} 142 143# Return list of paper types 144 145get_papers(): list of ref Paper 146{ 147 return all_papers; 148} 149 150# Return list of print options 151 152get_popts(): list of ref Popt 153{ 154 return all_popts; 155} 156 157# Save option settings 158 159save_settings(): int 160{ 161 return write_popt_config(all_popts); 162 163} 164 165 166# Print an image 167 168print_image(p: ref Printer, display: ref Draw->Display, im: ref Draw->Image, pcwidth: int, cancel: chan of int): int 169{ 170 if (p == nil || im == nil) return 1; 171 load_driver(p); 172 popen(p); 173 (xpix, ypix) := p.pdriver->printable_pixels(p); 174 imwidth := im.r.max.x - im.r.min.x; 175 imheight := im.r.max.y - im.r.min.y; 176 if (pcwidth > 0) pixwidth := int (real xpix * real pcwidth/100.0); 177 else pixwidth = imwidth; 178 lmar := (xpix - pixwidth)/2; 179 fpixwidth := pixwidth; 180 if (p.popt.orientation != PORTRAIT) { 181 lmar += pixwidth; 182 fpixwidth = pixwidth*imheight/imwidth; 183 } 184 if (lmar < 0) lmar = 0; 185 return p.pdriver->sendimage(p, printfd, display, im, fpixwidth, lmar, cancel); 186} 187 188# Print text 189 190print_textfd(p: ref Printer, fd: ref Sys->FD, ps: real, pr: int, wrap: int): int 191{ 192 load_driver(p); 193 popen(p); 194 return p.pdriver->sendtextfd(p, printfd, fd, ps, pr, wrap); 195 196} 197 198 199# Open printer device if necessary 200 201popen(p: ref Printer) 202{ 203 if (printfd != nil) return; 204 printfd = sys->create(p.device, Sys->OWRITE, DEFMODE); 205} 206 207# Find printer item 208 209find_printer(all: list of ref Printer, name: string): ref Printer 210{ 211 for (p:=all; p!=nil; p=tl p) if ((hd p).name == name) return hd p; 212 return nil; 213} 214 215# Find popt item 216 217find_popt(all: list of ref Popt, name: string): ref Popt 218{ 219 for (p:=all; p!=nil; p=tl p) if ((hd p).name == name) return hd p; 220 return nil; 221} 222 223 224# Find paper item 225 226find_paper(all: list of ref Paper, name: string): ref Paper 227{ 228 for (p:=all; p!=nil; p=tl p) if ((hd p).name == name) return hd p; 229 return nil; 230} 231 232# Find pmode item 233 234find_pmode(all: list of ref Pmode, name: string): ref Pmode 235{ 236 for (p:=all; p!=nil; p=tl p) if ((hd p).name == name) return hd p; 237 return nil; 238} 239 240# Find ptype item 241 242find_ptype(all: list of ref Ptype, name: string): ref Ptype 243{ 244 for (p:=all; p!=nil; p=tl p) if ((hd p).name == name) return hd p; 245 return nil; 246} 247 248 249# Read paper config file 250 251read_paper_config(): list of ref Paper 252{ 253 (clist, aliases) := read_config(PAPER_CONFIG); 254 rlist: list of ref Paper; 255 while (clist != nil) { 256 this := hd clist; 257 clist = tl clist; 258 item := ref Paper(this.name, "", 0.0, 0.0); 259 for (pairs:= this.pairs; pairs != nil; pairs = tl pairs) { 260 (name, value) := hd pairs; 261 case (name) { 262 "hpcode" => 263 item.hpcode = value; 264 265 "width_inches" => 266 item.width_inches = real value; 267 268 "height_inches" => 269 item.height_inches = real value; 270 271 * => 272 sys->fprint(stderr, "Unknown paper config file option: %s\n", name); 273 } 274 } 275 rlist =item :: rlist; 276 } 277 for (al:=aliases; al!=nil; al=tl al) { 278 (new, old) := hd al; 279 olda := find_paper(rlist, old); 280 if (olda == nil) sys->fprint(stderr, "Paper alias %s not found\n", old); 281 else { 282 newa := ref *olda; 283 newa.name = new; 284 rlist = newa :: rlist; 285 } 286 } 287 return rlist; 288} 289 290 291# Read pmode config file 292 293read_pmode_config(): list of ref Pmode 294{ 295 (clist, aliases) := read_config(PMODE_CONFIG); 296 rlist: list of ref Pmode; 297 while (clist != nil) { 298 this := hd clist; 299 clist = tl clist; 300 item := ref Pmode(this.name, "", 0, 0, 1, 1, 1); 301 for (pairs:= this.pairs; pairs != nil; pairs = tl pairs) { 302 (name, value) := hd pairs; 303 case (name) { 304 "desc" => 305 item.desc = value; 306 307 "resx" => 308 item.resx = int value; 309 310 "resy" => 311 item.resy = int value; 312 313 "coldepth" => 314 item.coldepth = int value; 315 316 "blackdepth" => 317 item.blackdepth = int value; 318 319 "blackresmult" => 320 item.blackresmult = int value; 321 322 * => 323 sys->fprint(stderr, "Unknown pmode config file option: %s\n", name); 324 325 } 326 } 327 rlist =item :: rlist; 328 } 329 for (al:=aliases; al!=nil; al=tl al) { 330 (new, old) := hd al; 331 olda := find_pmode(rlist, old); 332 if (olda == nil) sys->fprint(stderr, "Pmode alias %s not found\n", old); 333 else { 334 newa := ref *olda; 335 newa.name = new; 336 rlist = newa :: rlist; 337 } 338 } 339 return rlist; 340} 341 342 343 344 345# Readp Ptype config file 346 347read_ptype_config(): list of ref Ptype 348{ 349 (clist, aliases) := read_config(PTYPE_CONFIG); 350 rlist: list of ref Ptype; 351 while (clist != nil) { 352 this := hd clist; 353 clist = tl clist; 354 item := ref Ptype(this.name, "", nil, "", ""); 355 for (pairs:= this.pairs; pairs != nil; pairs = tl pairs) { 356 (name, value) := hd pairs; 357 case (name) { 358 "desc" => 359 item.desc = value; 360 361 "driver" => 362 item.driver = value; 363 364 "hpmapfile" => 365 item.hpmapfile = value; 366 367 "modes" => 368 item.modes = make_pmode_list(value); 369 370 * => 371 sys->fprint(stderr, "Unknown ptype config file option: %s\n", name); 372 } 373 } 374 if (item.modes == nil) { 375 sys->fprint(stderr, "No print modes for ptype %s\n", item.name); 376 continue; 377 } 378 rlist = item :: rlist; 379 } 380 for (al:=aliases; al!=nil; al=tl al) { 381 (new, old) := hd al; 382 olda := find_ptype(rlist, old); 383 if (olda == nil) sys->fprint(stderr, "Ptype alias %s not found\n", old); 384 else { 385 newa := ref *olda; 386 newa.name = new; 387 rlist = newa :: rlist; 388 } 389 } 390 return rlist; 391} 392 393 394# Make a list of pmodes from a string 395 396make_pmode_list(sl: string): list of ref Pmode 397{ 398 pml: list of ref Pmode; 399 (n, toks) := sys->tokenize(sl, " \t"); 400 if (n == 0) return nil; 401 for (i:=0; i<n; i++) { 402 pms := hd toks; 403 toks = tl toks; 404 pm := find_pmode(all_pmodes, pms); 405 if (pm == nil) { 406 sys->fprint(stderr, "unknown pmode: %s\n", pms); 407 continue; 408 } 409 pml = pm :: pml; 410 } 411 return pml; 412} 413 414 415# Read popt config file 416 417read_popt_config(): list of ref Popt 418{ 419 (clist, aliases) := read_config(POPT_CONFIG); 420 rlist: list of ref Popt; 421 while (clist != nil) { 422 this := hd clist; 423 clist = tl clist; 424 item := ref Popt(this.name, nil, nil, 0, 0); 425 for (pairs:= this.pairs; pairs != nil; pairs = tl pairs) { 426 (name, value) := hd pairs; 427 case (name) { 428 429 "mode" => 430 item.mode = find_pmode(all_pmodes, value); 431 if (item.mode == nil) sys->fprint(stderr, "Config error: Pmode not found: %s\n", value); 432 433 "paper" => 434 item.paper = find_paper(all_papers, value); 435 if (item.paper == nil) sys->fprint(stderr, "Config error: paper not found: %s\n", value); 436 437 "orientation" => 438 item.orientation = int value; 439 "duplex" => 440 item.duplex = int value; 441 442 * => 443 sys->fprint(stderr, "Unknown popt config file option: %s\n", name); 444 } 445 } 446 if (item.mode == nil) { 447 sys->fprint(stderr, "No print mode for printer %s\n", item.name); 448 continue; 449 } 450 if (item.paper == nil) { 451 sys->fprint(stderr, "No paper size for printer %s\n", item.name); 452 continue; 453 } 454 rlist = item :: rlist; 455 } 456 for (al:=aliases; al!=nil; al=tl al) { 457 (new, old) := hd al; 458 olda := find_popt(rlist, old); 459 if (olda == nil) sys->fprint(stderr, "Popt alias %s not found\n", old); 460 else { 461 newa := ref *olda; 462 newa.name = new; 463 rlist = newa :: rlist; 464 } 465 } 466 return rlist; 467} 468 469 470 471 472# Read printer config file 473 474read_printer_config(): list of ref Printer 475{ 476 (clist, aliases) := read_config(PRINTER_CONFIG); 477 rlist: list of ref Printer; 478 while (clist != nil) { 479 this := hd clist; 480 clist = tl clist; 481 item := ref Printer(this.name, nil, "", nil, nil); 482 for (pairs:= this.pairs; pairs != nil; pairs = tl pairs) { 483 (name, value) := hd pairs; 484 case (name) { 485 "ptype" => 486 item.ptype = find_ptype(all_ptypes, value); 487 if (item.ptype == nil) sys->fprint(stderr, "Config error: Ptype not found: %s\n", value); 488 489 "device" => 490 item.device = value; 491 492 * => 493 sys->fprint(stderr, "Unknown printer config file option: %s\n", name); 494 } 495 } 496 if (item.ptype == nil) { 497 sys->fprint(stderr, "No printer type for printer %s\n", item.name); 498 continue; 499 } 500 rlist = item :: rlist; 501 } 502 for (al:=aliases; al!=nil; al=tl al) { 503 (new, old) := hd al; 504 olda := find_printer(rlist, old); 505 if (olda == nil) sys->fprint(stderr, "Ptype alias %s not found\n", old); 506 else { 507 newa := ref *olda; 508 newa.name = new; 509 rlist = newa :: rlist; 510 } 511 } 512 return rlist; 513} 514 515# Write opt config file 516 517write_popt_config(plist: list of ref Popt): int 518{ 519 cfl: list of Cfg; 520 for (pl:=plist; pl!=nil; pl=tl pl) { 521 po := hd pl; 522 cf := Cfg(po.name, nil); 523 cf.pairs = ("mode", po.mode.name) :: cf.pairs; 524 cf.pairs = ("paper", po.paper.name) :: cf.pairs; 525 cf.pairs = ("orientation", sys->sprint("%d", po.orientation)) :: cf.pairs; 526 cf.pairs = ("duplex", sys->sprint("%d", po.duplex)) :: cf.pairs; 527 cfl = cf :: cfl; 528 } 529 return write_config(POPT_CONFIG, cfl, nil); 530} 531 532 533write_config(fspec: string, clist: list of Cfg, aliases: list of (string, string)): int 534{ 535 fd := sys->create(fspec, Sys->OWRITE, DEFMODE); 536 if (fd == nil) { 537 sys->fprint(stderr, "Failed to write to config file %s: %r\n", fspec); 538 return 1; 539 } 540 for (cfl:=clist; cfl!=nil; cfl=tl cfl) { 541 cf := hd cfl; 542 sys->fprint(fd, "%s=\n", cf.name); 543 for (pl:=cf.pairs; pl!=nil; pl=tl pl) { 544 (name, value) := hd pl; 545 if (sys->fprint(fd, "\t%s=%s\n", name, value) < 0) return 2; 546 } 547 } 548 for (al:=aliases; al!=nil; al=tl al) { 549 (new, old) := hd al; 550 if (sys->fprint(fd, "%s=%s\n", new, old)) return 2; 551 } 552 return 0; 553} 554 555 556# Read in a config file and return list of items and aliases 557 558read_config(fspec: string): (list of Cfg, list of (string, string)) 559{ 560 ib := bufio->open(fspec, Bufio->OREAD); 561 if (ib == nil) { 562 sys->fprint(stderr, "Failed to open config file %s: %r\n", fspec); 563 return (nil, nil); 564 } 565 clist: list of Cfg; 566 plist: list of (string, string); 567 section := ""; 568 aliases : list of (string, string); 569 while ((line := bufio->ib.gets('\n')) != nil) { 570 if (line[0] == '#') continue; 571 if (line[len line-1] == '\n') line = line[:len line-1]; 572 if (len line == 0) continue; 573 if (line[0] != ' ' && line[0] != '\t') { 574 if (section != "") clist = Cfg (section, plist) :: clist; 575 section = ""; 576 plist = nil; 577 sspec := strip(line); 578 (n, toks) := sys->tokenize(sspec, "="); 579 if (n == 0) continue; 580 if (n > 2) { 581 sys->fprint(stderr, "Error in config file %s\n", fspec); 582 continue; 583 } 584 if (n == 2) { 585 asection := hd toks; 586 toks = tl toks; 587 alias := hd toks; 588 aliases = (asection, alias) :: aliases; 589 continue; 590 } 591 section = hd toks; 592 } else { 593 (n, toks) := sys->tokenize(line, "="); 594 if (n == 2) { 595 name := strip(hd toks); 596 toks = tl toks; 597 value := strip(hd toks); 598 plist = (name, value) :: plist; 599 } 600 } 601 } 602 if (section != "") clist = Cfg (section, plist) :: clist; 603 return (clist, aliases); 604} 605 606 607# Load printer driver if necessary 608load_driver(p: ref Printer) 609{ 610 if (p.pdriver != nil) return; 611 modpath := Pdriver->PATHPREFIX + p.ptype.driver; 612 p.pdriver = load Pdriver modpath; 613 if (p.pdriver == nil) sys->fprint(stderr, "Failed to load driver %s: %r\n", modpath); 614 p.pdriver->init(DEBUG); 615} 616 617 618# Strip leading/trailing spaces 619 620strip(s: string): string 621{ 622 (dummy1, s1) := str->splitl(s, "^ \t"); 623 (s2, dummy2) := str->splitr(s1, "^ \t"); 624 return s2; 625} 626