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