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