xref: /inferno-os/appl/lib/print/print.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
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