xref: /plan9-contrib/sys/src/cmd/page/gfx.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1 /*
2  * graphics file reading for page
3  */
4 
5 #include <u.h>
6 #include <libc.h>
7 #include <draw.h>
8 #include <event.h>
9 #include <bio.h>
10 #include "page.h"
11 
12 typedef struct Convert	Convert;
13 typedef struct GfxInfo	GfxInfo;
14 typedef struct Graphic	Graphic;
15 
16 struct Convert {
17 	char *name;
18 	char *cmd;
19 	char *truecmd;	/* cmd for true color */
20 };
21 
22 struct GfxInfo {
23 	Graphic *g;
24 };
25 
26 struct Graphic {
27 	int type;
28 	char *name;
29 	uchar *buf;	/* if stdin */
30 	int nbuf;
31 };
32 
33 enum {
34 	Ipic,
35 	Itiff,
36 	Ijpeg,
37 	Igif,
38 	Iinferno,
39 	Ifax,
40 	Icvt2pic,
41 	Iplan9bm,
42 	Iccittg4,
43 	Ippm,
44 };
45 
46 Convert cvt[] = {
47 [Ipic]		{ "plan9",	"fb/3to1 rgbv %s |fb/pcp -tplan9" },
48 [Itiff]		{ "tiff",	"fb/tiff2pic %s | fb/3to1 rgbv | fb/pcp -tplan9" },
49 [Iplan9bm]	{ "plan9bm",	nil },
50 [Ijpeg]		{ "jpeg",	"jpg -9 %s", "jpg -t9 %s", },
51 [Igif]		{ "gif",	"gif -9 %s", "gif -t9 %s" },
52 [Iinferno]	{ "inferno",	nil },
53 [Ifax]		{ "fax",	"aux/g3p9bit -g %s" },
54 [Icvt2pic]	{ "unknown",	"fb/cvt2pic %s |fb/3to1 rgbv" },
55 [Ippm]		{ "ppm",	"ppm -9 %s", "ppm -t9 %s" },
56 /* ``temporary'' hack for hobby */
57 [Iccittg4]	{ "ccitt-g4",	"cat %s|rx nslocum /usr/lib/ocr/bin/bcp -M|fb/pcp -tcompressed -l0" },
58 };
59 
60 static Image*	convert(Graphic*);
61 static Image*	gfxdrawpage(Document *d, int page);
62 static char*	gfxpagename(Document*, int);
63 static int	spawnrc(char*, uchar*, int);
64 static void	waitrc(void);
65 static int	spawnpost(int);
66 static int	addpage(Document*, char*);
67 static int	rmpage(Document*, int);
68 static int	genaddpage(Document*, char*, uchar*, int);
69 
70 static char*
71 gfxpagename(Document *doc, int page)
72 {
73 	GfxInfo *gfx = doc->extra;
74 	return gfx->g[page].name;
75 }
76 
77 static Image*
78 gfxdrawpage(Document *doc, int page)
79 {
80 	GfxInfo *gfx = doc->extra;
81 	return convert(gfx->g+page);
82 }
83 
84 Document*
85 initgfx(Biobuf*, int argc, char **argv, uchar *buf, int nbuf)
86 {
87 	GfxInfo *gfx;
88 	Document *doc;
89 	int i;
90 
91 	doc = emalloc(sizeof(*doc));
92 	gfx = emalloc(sizeof(*gfx));
93 	gfx->g = nil;
94 
95 	doc->npage = 0;
96 	doc->drawpage = gfxdrawpage;
97 	doc->pagename = gfxpagename;
98 	doc->addpage = addpage;
99 	doc->rmpage = rmpage;
100 	doc->extra = gfx;
101 	doc->fwdonly = 0;
102 
103 	fprint(2, "reading through graphics...\n");
104 	if(argc==0 && buf)
105 		genaddpage(doc, nil, buf, nbuf);
106 	else{
107 		for(i=0; i<argc; i++)
108 			if(addpage(doc, argv[i]) < 0)
109 				fprint(2, "warning: not including %s: %r\n", argv[i]);
110 	}
111 
112 	return doc;
113 }
114 
115 static int
116 genaddpage(Document *doc, char *name, uchar *buf, int nbuf)
117 {
118 	Graphic *g;
119 	GfxInfo *gfx;
120 	Biobuf *b;
121 	uchar xbuf[32];
122 	int i;
123 
124 	gfx = doc->extra;
125 
126 	assert((name == nil) ^ (buf == nil));
127 	assert(name != nil || doc->npage == 0);
128 
129 	for(i=0; i<doc->npage; i++)
130 		if(strcmp(gfx->g[i].name, name) == 0)
131 			return i;
132 
133 	if(name){
134 		if((b = Bopen(name, OREAD)) == nil) {
135 			werrstr("Bopen: %r");
136 			return -1;
137 		}
138 
139 		if(Bread(b, xbuf, sizeof xbuf) != sizeof xbuf) {
140 			werrstr("short read: %r");
141 			return -1;
142 		}
143 		Bterm(b);
144 		buf = xbuf;
145 		nbuf = sizeof xbuf;
146 	}
147 
148 
149 	gfx->g = erealloc(gfx->g, (doc->npage+1)*(sizeof(*gfx->g)));
150 	g = &gfx->g[doc->npage];
151 
152 	memset(g, 0, sizeof *g);
153 	if(memcmp(buf, "GIF", 3) == 0)
154 		g->type = Igif;
155 	else if(memcmp(buf, "\111\111\052\000", 4) == 0)
156 		g->type = Itiff;
157 	else if(memcmp(buf, "\115\115\000\052", 4) == 0)
158 		g->type = Itiff;
159 	else if(memcmp(buf, "\377\330\377", 3) == 0)
160 		g->type = Ijpeg;
161 	else if(memcmp(buf, "compressed\n", 11) == 0)
162 		g->type = Iinferno;
163 	else if(memcmp(buf, "\0PC Research, Inc", 17) == 0)
164 		g->type = Ifax;
165 	else if(memcmp(buf, "TYPE=ccitt-g31", 14) == 0)
166 		g->type = Ifax;
167 	else if(memcmp(buf, "II*", 3) == 0)
168 		g->type = Ifax;
169 	else if(memcmp(buf, "TYPE=ccitt-g4", 13) == 0)
170 		g->type = Iccittg4;
171 	else if(memcmp(buf, "TYPE=", 5) == 0)
172 		g->type = Ipic;
173 	else if(buf[0] == 'P' && '0' <= buf[1] && buf[1] <= '9')
174 		g->type = Ippm;
175 	else if(memcmp(buf, "          ", 10) == 0 &&
176 		'0' <= buf[10] && buf[10] <= '9' &&
177 		buf[11] == ' ')
178 		g->type = Iplan9bm;
179 	else if(strtochan((char*)buf) != 0)
180 		g->type = Iplan9bm;
181 	else
182 		g->type = Icvt2pic;
183 
184 	if(name)
185 		g->name = estrdup(name);
186 	else{
187 		g->name = estrdup("stdin");	/* so it can be freed */
188 		g->buf = buf;
189 		g->nbuf = nbuf;
190 	}
191 
192 	if(chatty) fprint(2, "classified \"%s\" as \"%s\"\n", g->name, cvt[g->type].name);
193 	return doc->npage++;
194 }
195 
196 static int
197 addpage(Document *doc, char *name)
198 {
199 	return genaddpage(doc, name, nil, 0);
200 }
201 
202 static int
203 rmpage(Document *doc, int n)
204 {
205 	int i;
206 	GfxInfo *gfx;
207 
208 	if(n < 0 || n >= doc->npage)
209 		return -1;
210 
211 	gfx = doc->extra;
212 	doc->npage--;
213 	free(gfx->g[n].name);
214 
215 	for(i=n; i<doc->npage; i++)
216 		gfx->g[i] = gfx->g[i+1];
217 
218 	if(n < doc->npage)
219 		return n;
220 	if(n == 0)
221 		return 0;
222 	return n-1;
223 }
224 
225 
226 static Image*
227 convert(Graphic *g)
228 {
229 	int fd;
230 	Convert c;
231 	char *cmd;
232 	char *name, buf[1000];
233 	Image *im;
234 	int rcspawned = 0;
235 	Waitmsg w;
236 
237 	c = cvt[g->type];
238 	if(c.cmd == nil) {
239 		if(chatty) fprint(2, "no conversion for bitmap \"%s\"...\n", g->name);
240 		if(g->buf == nil){	/* not stdin */
241 			fd = open(g->name, OREAD);
242 			if(fd < 0) {
243 				fprint(2, "cannot open file: %r\n");
244 				wexits("open");
245 			}
246 		}else
247 			fd = stdinpipe(g->buf, g->nbuf);
248 	} else {
249 		cmd = c.cmd;
250 		if(truecolor && c.truecmd)
251 			cmd = c.truecmd;
252 
253 		if(g->buf != nil)	/* is stdin */
254 			name = "";
255 		else
256 			name = g->name;
257 		if(strlen(cmd)+strlen(name) > sizeof buf) {
258 			fprint(2, "command too long\n");
259 			wexits("convert");
260 		}
261 		snprint(buf, sizeof buf, cmd, name);
262 		if(chatty) fprint(2, "using \"%s\" to convert \"%s\"...\n", buf, g->name);
263 		fd = spawnrc(buf, g->buf, g->nbuf);
264 		rcspawned++;
265 		if(fd < 0) {
266 			fprint(2, "cannot spawn converter: %r\n");
267 			wexits("convert");
268 		}
269 	}
270 
271 	im = readimage(display, fd, 0);
272 	if(im == nil) {
273 		fprint(2, "warning: couldn't read image: %r\n");
274 	}
275 	close(fd);
276 
277 	/* for some reason rx doesn't work well with wait */
278 	/* for some reason 3to1 wexits on success with a non-null status of |3to1 */
279 	if(rcspawned && g->type != Iccittg4) {
280 		if(wait(&w)>=0 && w.msg[0] != '\0' && !strstr(w.msg, "3to1"))
281 			fprint(2, "slave wait error: %s\n", w.msg);
282 	}
283 	return im;
284 }
285 
286 static int
287 spawnrc(char *cmd, uchar *stdinbuf, int nstdinbuf)
288 {
289 	int pfd[2];
290 	int pid;
291 
292 	if(chatty) fprint(2, "spawning(%s)...", cmd);
293 
294 	if(pipe(pfd) < 0)
295 		return -1;
296 	if((pid = fork()) < 0)
297 		return -1;
298 
299 	if(pid == 0) {
300 		close(pfd[1]);
301 		if(stdinbuf)
302 			dup(stdinpipe(stdinbuf, nstdinbuf), 0);
303 		else
304 			dup(open("/dev/null", OREAD), 0);
305 		dup(pfd[0], 1);
306 		//dup(pfd[0], 2);
307 		execl("/bin/rc", "rc", "-c", cmd, nil);
308 		wexits("exec");
309 	}
310 	close(pfd[0]);
311 	return pfd[1];
312 }
313 
314