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