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