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