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