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