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