xref: /inferno-os/appl/lib/pslib.b (revision 9b29ac7ea714507a9c0690620c02c8ca5ab25f90)
1implement Pslib;
2
3include "sys.m";
4	sys: Sys;
5
6include "draw.m";
7	draw : Draw;
8Image, Display,Rect,Point : import draw;
9
10include "bufio.m";
11	bufmod : Bufio;
12
13include "tk.m";
14	tk: Tk;
15	Toplevel: import tk;
16
17Iobuf : import bufmod;
18
19include "string.m";
20	str : String;
21
22include "daytime.m";
23	time : Daytime;
24
25include "pslib.m";
26
27# old module declaration.
28# this whole thing needs a revamp, so almost all the old external
29# linkages have been removed until there's time to do it properly.
30#Pslib : module
31#{
32#	PATH:		con "/dis/lib/pslib.dis";
33#
34#	init:	fn(env: ref Draw->Context, t: ref Tk->Toplevel, boxes: int, deb: int): string;
35#	getfonts:		fn(input: string): string;
36#	preamble:		fn(ioutb: ref Bufio->Iobuf, bbox: Draw->Rect): string;
37#	trailer:		fn(ioutb: ref Bufio->Iobuf, pages: int): string;
38#	printnewpage:	fn(pagenum: int, end: int, ioutb: ref Bufio->Iobuf);
39#	parseTkline:	fn(ioutb: ref Bufio->Iobuf, input: string): string;
40#	stats:		fn(): (int, int, int);
41#	deffont:		fn(): string;
42#	image2psfile:	fn(ioutb: ref Bufio->Iobuf, im: ref Draw->Image, dpi: int) : string;
43#};
44
45ASCII,RUNE,IMAGE : con iota;
46
47Iteminfo : adt
48{
49	itype: int;
50	offset: int;		# offset from the start of line.
51	width: int;		# width....
52	ascent: int;	# ascent of the item
53	font: int;		# font
54	line : int;		# line its on
55	buf : string;
56};
57
58Lineinfo : adt
59{
60	xorg: int;
61	yorg: int;
62	width: int;
63	height: int;
64	ascent: int;
65};
66
67
68font_arr := array[256] of {* => (-1,"")};
69remap := array[20] of (string,string);
70
71PXPI : con 100;
72PTPI : con 100;
73
74boxes: int;
75debug: int;
76totitems: int;
77totlines: int;
78curfont: int;
79def_font: string;
80def_font_type: int;
81curfonttype: int;
82pagestart: int;
83ctxt: ref Draw->Context;
84t: ref Toplevel;
85
86nomod(s: string)
87{
88	sys->print("pslib: cannot load %s: %r\n", s);
89	raise "fail:bad module";
90}
91
92init(bufio: Bufio)
93{
94	sys = load Sys Sys->PATH;
95	draw = load Draw Draw->PATH;
96	tk= load Tk Tk->PATH;
97	if (tk == nil)
98		nomod(Tk->PATH);
99	str = load String String->PATH;
100	if (str == nil)
101		nomod(String->PATH);
102	bufmod = bufio;
103}
104
105
106oldinit(env: ref Draw->Context, d: ref Toplevel, nil: int,deb: int): string
107{
108	sys = load Sys Sys->PATH;
109	str = load String String->PATH;
110	draw = load Draw Draw->PATH;
111	tk= load Tk Tk->PATH;
112	bufmod = load Bufio Bufio->PATH;
113	ctxt=env;
114	t=d;
115	debug = deb;
116	totlines=0;
117	totitems=0;
118	pagestart=0;
119	boxes=0; #box;
120	curfont=0;
121	e := loadfonts();
122	if (e != "")
123		return e;
124	return "";
125}
126
127stats(): (int,int,int)
128{
129	return (totitems,totlines,curfont);
130}
131
132loadfonts() : string
133{
134	input : string;
135	iob:=bufmod->open("/fonts/psrename",bufmod->OREAD);
136	if (iob==nil)
137		return sys->sprint("can't open /fonts/psrename: %r");
138	i:=0;
139	while((input=iob.gets('\n'))!=nil){
140		(tkfont,psfont):=str->splitl(input," ");
141		psfont=psfont[1:len psfont -1];
142		remap[i]=(tkfont,psfont);
143		i++;
144	}
145	return "";
146}
147
148preamble(ioutb: ref Iobuf, bb: Rect)
149{
150	time = load Daytime Daytime->PATH;
151	username := "";
152	fd := sys->open("/dev/user", sys->OREAD);
153	if(fd != nil) {
154		b := array[128] of byte;
155		n := sys->read(fd, b, len b);
156		b=b[0:n];
157		username = string b;
158		fd = nil;
159	}
160	if(bb.max.x == 0 && bb.max.y == 0) {
161		bb.max.x = 612;
162		bb.max.y = 792;
163	}
164	ioutb.puts("%!PS-Adobe-3.0\n");
165	ioutb.puts(sys->sprint("%%%%Creator: Pslib 1.0 (%s)\n",username));
166	ioutb.puts(sys->sprint("%%%%CreationDate: %s\n",time->time()));
167	ioutb.puts("%%Pages: (atend) \n");
168	ioutb.puts(sys->sprint("%%%%BoundingBox: %d %d %d %d\n", bb.min.x, bb.min.y, bb.max.x, bb.max.y));
169	ioutb.puts("%%EndComments\n");
170	ioutb.puts("%%BeginProlog\n");
171	ioutb.puts("/doimage {\n");
172	ioutb.puts("/bps exch def\n");
173	ioutb.puts("/width exch def\n");
174	ioutb.puts("/height exch def\n");
175	ioutb.puts("/xstart exch def\n");
176	ioutb.puts("/ystart exch def\n");
177	ioutb.puts("/iwidth exch def\n");
178	ioutb.puts("/ascent exch def\n");
179	ioutb.puts("/iheight exch def\n");
180	ioutb.puts("gsave\n");
181	if(boxes)
182		ioutb.puts("xstart ystart iwidth iheight rectstroke\n");
183	# if bps==8, use inferno colormap; else (bps < 8) it's grayscale
184	ioutb.puts("bps 8 eq\n");
185	ioutb.puts("{\n");
186	ioutb.puts("[/Indexed /DeviceRGB 255 \n");
187	ioutb.puts("<ffffff ffffaa ffff55 ffff00 ffaaff ffaaaa ffaa55 ffaa00 ff55ff ff55aa ff5555 ff5500\n");
188	ioutb.puts("ff00ff ff00aa ff0055 ff0000 ee0000 eeeeee eeee9e eeee4f eeee00 ee9eee ee9e9e ee9e4f\n");
189	ioutb.puts("ee9e00 ee4fee ee4f9e ee4f4f ee4f00 ee00ee ee009e ee004f dd0049 dd0000 dddddd dddd93\n");
190	ioutb.puts("dddd49 dddd00 dd93dd dd9393 dd9349 dd9300 dd49dd dd4993 dd4949 dd4900 dd00dd dd0093\n");
191	ioutb.puts("cc0088 cc0044 cc0000 cccccc cccc88 cccc44 cccc00 cc88cc cc8888 cc8844 cc8800 cc44cc\n");
192	ioutb.puts("cc4488 cc4444 cc4400 cc00cc aaffaa aaff55 aaff00 aaaaff bbbbbb bbbb5d bbbb00 aa55ff\n");
193	ioutb.puts("bb5dbb bb5d5d bb5d00 aa00ff bb00bb bb005d bb0000 aaffff 9eeeee 9eee9e 9eee4f 9eee00\n");
194	ioutb.puts("9e9eee aaaaaa aaaa55 aaaa00 9e4fee aa55aa aa5555 aa5500 9e00ee aa00aa aa0055 aa0000\n");
195	ioutb.puts("990000 93dddd 93dd93 93dd49 93dd00 9393dd 999999 99994c 999900 9349dd 994c99 994c4c\n");
196	ioutb.puts("994c00 9300dd 990099 99004c 880044 880000 88cccc 88cc88 88cc44 88cc00 8888cc 888888\n");
197	ioutb.puts("888844 888800 8844cc 884488 884444 884400 8800cc 880088 55ff55 55ff00 55aaff 5dbbbb\n");
198	ioutb.puts("5dbb5d 5dbb00 5555ff 5d5dbb 777777 777700 5500ff 5d00bb 770077 770000 55ffff 55ffaa\n");
199	ioutb.puts("4fee9e 4fee4f 4fee00 4f9eee 55aaaa 55aa55 55aa00 4f4fee 5555aa 666666 666600 4f00ee\n");
200	ioutb.puts("5500aa 660066 660000 4feeee 49dddd 49dd93 49dd49 49dd00 4993dd 4c9999 4c994c 4c9900\n");
201	ioutb.puts("4949dd 4c4c99 555555 555500 4900dd 4c0099 550055 550000 440000 44cccc 44cc88 44cc44\n");
202	ioutb.puts("44cc00 4488cc 448888 448844 448800 4444cc 444488 444444 444400 4400cc 440088 440044\n");
203	ioutb.puts("00ff00 00aaff 00bbbb 00bb5d 00bb00 0055ff 005dbb 007777 007700 0000ff 0000bb 000077\n");
204	ioutb.puts("333333 00ffff 00ffaa 00ff55 00ee4f 00ee00 009eee 00aaaa 00aa55 00aa00 004fee 0055aa\n");
205	ioutb.puts("006666 006600 0000ee 0000aa 000066 222222 00eeee 00ee9e 00dd93 00dd49 00dd00 0093dd\n");
206	ioutb.puts("009999 00994c 009900 0049dd 004c99 005555 005500 0000dd 000099 000055 111111 00dddd\n");
207	ioutb.puts("00cccc 00cc88 00cc44 00cc00 0088cc 008888 008844 008800 0044cc 004488 004444 004400\n");
208	ioutb.puts("0000cc 000088 000044 000000>\n");
209	ioutb.puts("] setcolorspace\n");
210	ioutb.puts("/decodemat [0 255] def\n");
211	ioutb.puts("}\n");
212	# else, bps != 8
213	ioutb.puts("{\n");
214	ioutb.puts("[/DeviceGray] setcolorspace\n");
215	ioutb.puts("/decodemat [1 0] def\n");
216	ioutb.puts("}\n");
217	ioutb.puts("ifelse\n");
218	ioutb.puts("xstart ystart translate \n");
219	ioutb.puts("iwidth iheight scale \n");
220	ioutb.puts("<<\n");
221	ioutb.puts("/ImageType 1\n");
222	ioutb.puts("/Width width \n");
223	ioutb.puts("/Height height \n");
224	ioutb.puts("/BitsPerComponent bps %bits/sample\n");
225	ioutb.puts("/Decode decodemat % Inferno cmap or DeviceGray value\n");
226	ioutb.puts("/ImageMatrix [width 0 0 height neg 0 height]\n");
227	ioutb.puts("/DataSource currentfile /ASCII85Decode filter\n");
228	ioutb.puts(">> \n");
229	ioutb.puts("image\n");
230	ioutb.puts("grestore\n");
231	ioutb.puts("} def\n");
232	ioutb.puts("%%EndProlog\n");
233}
234
235trailer(ioutb : ref Iobuf,pages : int)
236{
237	ioutb.puts("%%Trailer\n%%Pages: "+string pages+"\n%%EOF\n");
238}
239
240
241printnewpage(pagenum : int,end : int, ioutb : ref Iobuf)
242{
243	pnum:=string pagenum;
244	if (end){
245		# bounding box
246		if (boxes){
247			ioutb.puts("18 18 moveto 594 18 lineto 594 774 lineto 18 774 lineto"+
248								" closepath stroke\n");
249		}
250		ioutb.puts("showpage\n%%EndPage "+pnum+" "+pnum+"\n");
251	} else
252		ioutb.puts("%%Page: "+pnum+" "+pnum+"\n");
253}
254
255printimage(ioutb: ref Iobuf, line: Lineinfo, imag: Iteminfo): (string,string)
256{
257	RM:=612-18;
258	class:=tk->cmd(t,"winfo class "+imag.buf);
259#sys->print("Looking for [%s] of type [%s]\n",imag.buf,class);
260	if (line.xorg+imag.offset+imag.width>RM)
261		imag.width=RM-line.xorg-imag.offset;
262	case class {
263		"button" or "menubutton" =>
264			# try to get the text out and print it....
265			ioutb.puts(sys->sprint("%d %d moveto\n",line.xorg+imag.offset,
266							line.yorg));
267			msg:=tk->cmd(t,sys->sprint("%s cget -text",imag.buf));
268			ft:=tk->cmd(t,sys->sprint("%s cget -font",imag.buf));
269			sys->print("font is [%s]\n",ft);
270			ioutb.puts(sys->sprint("%d %d %d %d rectstroke\n",
271						line.xorg+imag.offset,line.yorg,imag.width,
272						line.height));
273			return (class,msg);
274		"label" =>
275			(im,nil,nil) := tk->getimage(t,imag.buf);
276			if (im!=nil){
277				bps := im.depth;
278				ioutb.puts(sys->sprint("%d %d %d %d %d %d %d %d doimage\n",
279						im.r.dy(),line.ascent,im.r.dx(),line.yorg,
280						line.xorg+imag.offset,im.r.dy(), im.r.dx(), bps));
281				imagebits(ioutb,im);
282			}
283			return (class,"");
284		"entry" =>
285			ioutb.puts(sys->sprint("%d %d moveto\n",line.xorg+imag.offset,
286					line.yorg));
287			ioutb.puts(sys->sprint("%d %d %d %d rectstroke\n",
288					line.xorg+imag.offset,line.yorg,imag.width,
289					line.height));
290			return (class,"");
291		* =>
292			sys->print("Unhandled class [%s]\n",class);
293			return (class,"Error");
294
295	}
296	return ("","");
297}
298
299printline(ioutb: ref Iobuf,line : Lineinfo,items : array of Iteminfo)
300{
301	xstart:=line.xorg;
302	wid:=xstart;
303	# items
304	if (len items == 0) return;
305	for(j:=0;j<len items;j++){
306		msg:="";
307		class:="";
308		if (items[j].itype==IMAGE)
309			(class,msg)=printimage(ioutb,line,items[j]);
310		if (items[j].itype!=IMAGE || class=="button"|| class=="menubutton"){
311			setfont(ioutb,items[j].font);
312			if (msg!=""){
313				# position the text in the center of the label
314				# moveto curpoint
315				# (msg) stringwidth pop xstart sub 2 div
316				ioutb.puts(sys->sprint("%d %d moveto\n",xstart+items[j].offset,
317						line.yorg+line.height-line.ascent));
318				ioutb.puts(sys->sprint("(%s) dup stringwidth pop 2 div",
319								msg));
320				ioutb.puts(" 0 rmoveto show\n");
321			}
322			else {
323				ioutb.puts(sys->sprint("%d %d moveto\n",
324					xstart+items[j].offset,line.yorg+line.height
325					-line.ascent));
326				ioutb.puts(sys->sprint("(%s) show\n",items[j].buf));
327			}
328		}
329		wid=xstart+items[j].offset+items[j].width;
330	}
331	if (boxes)
332		ioutb.puts(sys->sprint("%d %d %d %d rectstroke\n",line.xorg,line.yorg,
333									wid,line.height));
334}
335
336setfont(ioutb: ref Iobuf, font: int)
337{
338	ftype : int;
339	fname : string;
340	if ((curfonttype & font) != curfonttype){
341		for(f:=0;f<curfont;f++){
342			(ftype,fname)=font_arr[f];
343				if ((ftype&font)==ftype)
344					break;
345		}
346		if (f==curfont){
347			fname=def_font;
348			ftype=def_font_type;
349		}
350		ioutb.puts(sys->sprint("%s setfont\n",fname));
351		curfonttype=ftype;
352	}
353}
354
355parseTkline(ioutb: ref Iobuf, input: string): string
356{
357	thisline : Lineinfo;
358	PS:=792-18-18;	# page size in points
359	TM:=792-18;	# top margin in points
360	LM:=18;		# left margin 1/4 in. in
361#	BM:=18;		# bottom margin 1/4 in. in
362	x : int;
363	(x,input)=str->toint(input,10);
364	thisline.xorg=(x*PTPI)/PXPI;
365	(x,input)=str->toint(input,10);
366	thisline.yorg=(x*PTPI)/PXPI;
367	(x,input)=str->toint(input,10);
368	thisline.width=(x*PTPI)/PXPI;
369	(x,input)=str->toint(input,10);
370	thisline.height=(x*PTPI)/PXPI;
371	(x,input)=str->toint(input,10);
372	thisline.ascent=(x*PTPI)/PXPI;
373	(x,input)=str->toint(input,10);
374	# thisline.numitems=x;
375	if (thisline.width==0 || thisline.height==0)
376		return "";
377	if (thisline.yorg+thisline.height-pagestart>PS){
378		pagestart=thisline.yorg;
379		return "newpage";
380		# must resend this line....
381	}
382	thisline.yorg=TM-thisline.yorg-thisline.height+pagestart;
383	thisline.xorg+=LM;
384	(items, err) :=getline(totlines,input);
385	if(err != nil)
386		return err;
387	totitems+=len items;
388	totlines++;
389	printline(ioutb,thisline,items);
390	return "";
391}
392
393getfonts(input: string) : string
394{
395	tkfont,psfont : string;
396	j : int;
397	retval := "";
398	if (input[0]=='%')
399			return "";
400	# get a line of the form
401	# 5::/fonts/lucida/moo.16.font
402	# translate it to...
403	# 32 f32.16
404	# where 32==1<<5 and f32.16 is a postscript function that loads the
405	# appropriate postscript font (from remap)
406	# and writes it to fonts....
407	(bits,font):=str->toint(input,10);
408	if (bits!=-1)
409		bits=1<<bits;
410	else{
411		bits=1;
412		def_font_type=bits;
413		curfonttype=def_font_type;
414	}
415	font=font[2:];
416	for(i:=0;i<len remap;i++){
417		(tkfont,psfont)=remap[i];
418		if (tkfont==font)
419			break;
420	}
421	if (i==len remap)
422		psfont="Times-Roman";
423	(font,nil)=str->splitr(font,".");
424	(nil,font)=str->splitr(font[0:len font-1],".");
425	(fsize,nil):=str->toint(font,10);
426	fsize=(PTPI*3*fsize)/(2*PXPI);
427	enc_font:="f"+string bits+"."+string fsize;
428	ps_func:="/"+enc_font+" /"+psfont+" findfont "+string fsize+
429							" scalefont def\n";
430	sy_font:="sy"+string fsize;
431	xtra_func:="/"+sy_font+" /Symbol findfont "+string fsize+
432							" scalefont def\n";
433	for(i=0;i<len font_arr;i++){
434		(j,font)=font_arr[i];
435		if (j==-1) break;
436	}
437	if (j==len font_arr)
438		return "Error";
439	font_arr[i]=(bits,enc_font);
440	if (bits==1)
441		def_font=enc_font;
442	curfont++;
443	retval+= ps_func;
444	retval+= xtra_func;
445	return retval;
446}
447
448deffont() : string
449{
450	return def_font;
451}
452
453getline(k : int,  input : string) : (array of Iteminfo, string)
454{
455	lineval,args : string;
456	j, nb : int;
457	lw:=0;
458	wid:=0;
459	flags:=0;
460	item_arr := array[32] of {* => Iteminfo(-1,-1,-1,-1,-1,-1,"")};
461	curitem:=0;
462	while(input!=nil){
463		(nil,input)=str->splitl(input,"[");
464		if (input==nil)
465			break;
466		com:=input[1];
467		input=input[2:];
468		case com {
469		'A' =>
470			nb=0;
471			# get the width of the item
472			(wid,input)=str->toint(input,10);
473			wid=(wid*PTPI)/PXPI;
474			if (input[0]!='{')
475				return (nil, sys->sprint(
476					"line %d item %d Bad Syntax : '{' expected",
477						k,curitem));
478			# get the args.
479			(args,input)=str->splitl(input,"}");
480			# get the flags.
481			# assume there is only one int flag..
482			(flags,args)=str->toint(args[1:],16);
483			if (args!=nil && debug){
484				sys->print("line %d item %d extra flags=%s\n",
485						k,curitem,args);
486			}
487			if (flags<1024) flags=1;
488			item_arr[curitem].font=flags;
489			item_arr[curitem].offset=lw;
490			item_arr[curitem].width=wid;
491			lw+=wid;
492			for(j=1;j<len input;j++){
493				if ((input[j]==')')||(input[j]=='('))
494						lineval[len lineval]='\\';
495				if (input[j]=='[')
496					nb++;
497				if (input[j]==']')
498					if (nb==0)
499						break;
500					else
501						nb--;
502				lineval[len lineval]=input[j];
503			}
504			if (j<len input)
505				input=input[j:];
506			item_arr[curitem].buf=lineval;
507			item_arr[curitem].line=k;
508			item_arr[curitem].itype=ASCII;
509			curitem++;
510			lineval="";
511		'R' =>
512			nb=0;
513			# get the width of the item
514			(wid,input)=str->toint(input,10);
515			wid=(wid*PTPI)/PXPI;
516			if (input[0]!='{')
517				return (nil, "Bad Syntax : '{' expected");
518			# get the args.
519			(args,input)=str->splitl(input,"}");
520			# get the flags.
521			# assume there is only one int flag..
522			(flags,args)=str->toint(args[1:],16);
523			if (args!=nil && debug){
524				sys->print("line %d item %d Bad Syntax args=%s",
525						k,curitem,args);
526			}
527			item_arr[curitem].font=flags;
528			item_arr[curitem].offset=lw;
529			item_arr[curitem].width=wid;
530			lw+=wid;
531			for(j=1;j<len input;j++){
532				if (input[j]=='[')
533					nb++;
534				if (input[j]==']')
535					if (nb==0)
536						break;
537					else
538						nb--;
539				case input[j] {
540					8226 => # bullet
541						lineval+="\\267 ";
542					169 =>  # copyright
543						lineval+="\\251 ";
544						curitem++;
545					* =>
546						lineval[len lineval]=input[j];
547				}
548			}
549			if (j>len input)
550				input=input[j:];
551			item_arr[curitem].buf=lineval;
552			item_arr[curitem].line=k;
553			item_arr[curitem].itype=RUNE;
554			curitem++;
555			lineval="";
556		'N' or 'C'=>
557			# next item
558			for(j=0;j<len input;j++)
559				if (input[j]==']')
560					break;
561			if (j>len input)
562				input=input[j:];
563		'T' =>
564			(wid,input)=str->toint(input,10);
565			wid=(wid*PTPI)/PXPI;
566			item_arr[curitem].offset=lw;
567			item_arr[curitem].width=wid;
568			lw+=wid;
569			lineval[len lineval]='\t';
570			# next item
571			for(j=0;j<len input;j++)
572				if (input[j]==']')
573					break;
574			if (j>len input)
575				input=input[j:];
576			item_arr[curitem].buf=lineval;
577			item_arr[curitem].line=k;
578			item_arr[curitem].itype=ASCII;
579			curitem++;
580			lineval="";
581		'W' =>
582			(wid,input)=str->toint(input,10);
583			wid=(wid*PTPI)/PXPI;
584			item_arr[curitem].offset=lw;
585			item_arr[curitem].width=wid;
586			item_arr[curitem].itype=IMAGE;
587			lw+=wid;
588			# next item
589			for(j=1;j<len input;j++){
590				if (input[j]==']')
591					break;
592				lineval[len lineval]=input[j];
593			}
594			item_arr[curitem].buf=lineval;
595			if (j>len input)
596				input=input[j:];
597			curitem++;
598			lineval="";
599		* =>
600			# next item
601			for(j=0;j<len input;j++)
602				if (input[j]==']')
603					break;
604			if (j>len input)
605				input=input[j:];
606
607		}
608	}
609	return (item_arr[0:curitem], "");
610}
611
612writeimage(ioutb: ref Iobuf, im: ref Draw->Image, dpi: int)
613{
614	r := im.r;
615	width := r.dx();
616	height := r.dy();
617	iwidth := width * 72 / dpi;
618	iheight := height * 72 / dpi;
619	xstart := 72;
620	ystart := 720 - iheight;
621	bbox := Rect((xstart,ystart), (xstart+iwidth,ystart+iheight));
622	preamble(ioutb, bbox);
623	ioutb.puts("%%Page: 1\n%%BeginPageSetup\n");
624	ioutb.puts("/pgsave save def\n");
625	ioutb.puts("%%EndPageSetup\n");
626	bps := im.depth;
627	ioutb.puts(sys->sprint("%d 0 %d %d %d %d %d %d doimage\n", iheight, iwidth, ystart, xstart, height, width, bps));
628	imagebits(ioutb, im);
629	ioutb.puts("pgsave restore\nshowpage\n");
630	trailer(ioutb, 1);
631	ioutb.flush();
632}
633
634imagebits(ioutb: ref Iobuf, im: ref Draw->Image)
635{
636	if(debug)
637		sys->print("imagebits, r=%d %d %d %d, depth=%d\n",
638			im.r.min.x, im.r.min.y, im.r.max.x, im.r.max.y, im.depth);
639	width:=im.r.dx();
640	height:=im.r.dy();
641	bps:=im.depth;	# bits per sample
642	spb := 1;			# samples per byte
643	bitoff := 0;			# bit offset of beginning sample within first byte
644	linebytes := width;
645	if(bps < 8) {
646		spb=8/bps;
647		bitoff=(im.r.min.x % spb) * bps;
648		linebytes=(bitoff + (width-1)*bps) / 8 + 1;
649	}
650	arr:=array[linebytes*height] of byte;
651	n:=im.readpixels(im.r,arr);
652	if(debug)
653		sys->print("linebytes=%d, height=%d, readpixels returned %d\n",
654			linebytes, height, n);
655	if(n < 0) {
656		n = len arr;
657		for(i := 0; i < n; i++)
658			arr[i] = byte 0;
659	}
660	if(bitoff != 0) {
661		# Postscript image wants beginning of line at beginning of byte
662		pslinebytes := (width-1)*bps + 1;
663		if(debug)
664			sys->print("bitoff=%d, pslinebytes=%d\n", bitoff, pslinebytes);
665		old:=arr;
666		n = pslinebytes*height;
667		arr=array[n] of byte;
668		a0 := 0;
669		o0 := 0;
670		for(y := 0; y < height; y++) {
671			for(i:=0; i < pslinebytes; i++)
672				arr[a0+i] = (old[o0+i]<<bitoff) | (old[o0+i+1]>>(8-bitoff));
673			a0 += pslinebytes;
674			o0 += linebytes;
675		}
676	}
677	lsf:=0;
678	n4 := (n/4)*4;
679	for(i:=0;i<n4;i+=4){
680		s:=cmap2ascii85(arr[i:i+4]);
681		lsf+=len s;
682		ioutb.puts(s);
683		if (lsf>74){
684		  ioutb.puts("\n");
685		  lsf=0;
686		}
687	}
688	nrest:=n-n4;
689	if(nrest!=0){
690		foo:=array[4] of {* => byte 0};
691		foo[0:]=arr[n4:n];
692		s:=cmap2ascii85(foo);
693		if(s=="z")
694			s="!!!!!";
695		ioutb.puts(s[0:nrest+1]);
696	}
697	ioutb.puts("~>\n");
698	ioutb.flush();
699}
700
701
702cmap2ascii85(arr : array of byte) : string
703{
704	b := array[4] of {* => big 0};
705	for(i:=0;i<4;i++)
706		b[i]=big arr[i];
707	i1:=(b[0]<<24)+(b[1]<<16)+(b[2]<<8)+b[3];
708	c1:=sys->sprint("%c%c%c%c%c",'!'+int ((i1/big (85*85*85*85))%big 85),
709					'!'+int ((i1/big (85*85*85))%big 85),
710					'!'+int ((i1/big (85*85))% big 85),
711					'!'+int ((i1/big 85)% big 85),'!'+int(i1% big 85));
712	if (c1=="!!!!!") c1="z";
713	return c1;
714}
715