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 setlabel(gfxpagename(doc, page)); 91 GfxInfo *gfx = doc->extra; 92 return convert(gfx->g+page); 93 } 94 95 Document* 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 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 216 addpage(Document *doc, char *name) 217 { 218 return genaddpage(doc, name, nil, 0); 219 } 220 221 static int 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* 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 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