ps.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. /*
  2. * ps.c
  3. *
  4. * provide postscript file reading support for page
  5. */
  6. #include <u.h>
  7. #include <libc.h>
  8. #include <draw.h>
  9. #include <event.h>
  10. #include <bio.h>
  11. #include <ctype.h>
  12. #include "page.h"
  13. typedef struct PSInfo PSInfo;
  14. typedef struct Page Page;
  15. struct Page {
  16. char *name;
  17. int offset; /* offset of page beginning within file */
  18. };
  19. struct PSInfo {
  20. GSInfo;
  21. Rectangle bbox; /* default bounding box */
  22. Page *page;
  23. int npage;
  24. int clueless; /* don't know where page boundaries are */
  25. long psoff; /* location of %! in file */
  26. char ctm[256];
  27. };
  28. static int pswritepage(Document *d, int fd, int page);
  29. static Image* psdrawpage(Document *d, int page);
  30. static char* pspagename(Document*, int);
  31. #define R(r) (r).min.x, (r).min.y, (r).max.x, (r).max.y
  32. Rectangle
  33. rdbbox(char *p)
  34. {
  35. Rectangle r;
  36. int a;
  37. char *f[4];
  38. while(*p == ':' || *p == ' ' || *p == '\t')
  39. p++;
  40. if(tokenize(p, f, 4) != 4)
  41. return Rect(0,0,0,0);
  42. r = Rect(atoi(f[0]), atoi(f[1]), atoi(f[2]), atoi(f[3]));
  43. r = canonrect(r);
  44. if(Dx(r) <= 0 || Dy(r) <= 0)
  45. return Rect(0,0,0,0);
  46. if(truetoboundingbox)
  47. return r;
  48. /* initdraw not called yet, can't use %R */
  49. if(chatty) fprint(2, "[%d %d %d %d] -> ", R(r));
  50. /*
  51. * attempt to sniff out A4, 8½×11, others
  52. * A4 is 596×842
  53. * 8½×11 is 612×792
  54. */
  55. a = Dx(r)*Dy(r);
  56. if(a < 300*300){ /* really small, probably supposed to be */
  57. /* empty */
  58. } else if(Dx(r) <= 596 && r.max.x <= 596 && Dy(r) > 792 && Dy(r) <= 842 && r.max.y <= 842) /* A4 */
  59. r = Rect(0, 0, 596, 842);
  60. else { /* cast up to 8½×11 */
  61. if(Dx(r) <= 612 && r.max.x <= 612){
  62. r.min.x = 0;
  63. r.max.x = 612;
  64. }
  65. if(Dy(r) <= 792 && r.max.y <= 792){
  66. r.min.y = 0;
  67. r.max.y = 792;
  68. }
  69. }
  70. if(chatty) fprint(2, "[%d %d %d %d]\n", R(r));
  71. return r;
  72. }
  73. #define RECT(X) X.min.x, X.min.y, X.max.x, X.max.y
  74. int
  75. prefix(char *x, char *y)
  76. {
  77. return strncmp(x, y, strlen(y)) == 0;
  78. }
  79. /*
  80. * document ps is really being printed as n-up pages.
  81. * we need to treat every n pages as 1.
  82. */
  83. void
  84. repaginate(PSInfo *ps, int n)
  85. {
  86. int i, np, onp;
  87. Page *page;
  88. page = ps->page;
  89. onp = ps->npage;
  90. np = (ps->npage+n-1)/n;
  91. if(chatty) {
  92. for(i=0; i<=onp+1; i++)
  93. print("page %d: %d\n", i, page[i].offset);
  94. }
  95. for(i=0; i<np; i++)
  96. page[i] = page[n*i];
  97. /* trailer */
  98. page[np] = page[onp];
  99. /* EOF */
  100. page[np+1] = page[onp+1];
  101. ps->npage = np;
  102. if(chatty) {
  103. for(i=0; i<=np+1; i++)
  104. print("page %d: %d\n", i, page[i].offset);
  105. }
  106. }
  107. Document*
  108. initps(Biobuf *b, int argc, char **argv, uchar *buf, int nbuf)
  109. {
  110. Document *d;
  111. PSInfo *ps;
  112. char *p;
  113. char *q, *r;
  114. char eol;
  115. char *nargv[1];
  116. char fdbuf[20];
  117. char tmp[32];
  118. int fd;
  119. int i;
  120. int incomments;
  121. int cantranslate;
  122. int trailer=0;
  123. int nesting=0;
  124. int dumb=0;
  125. int landscape=0;
  126. long psoff;
  127. long npage, mpage;
  128. Page *page;
  129. Rectangle bbox = Rect(0,0,0,0);
  130. if(argc > 1) {
  131. fprint(2, "can only view one ps file at a time\n");
  132. return nil;
  133. }
  134. fprint(2, "reading through postscript...\n");
  135. if(b == nil){ /* standard input; spool to disk (ouch) */
  136. fd = spooltodisk(buf, nbuf, nil);
  137. sprint(fdbuf, "/fd/%d", fd);
  138. b = Bopen(fdbuf, OREAD);
  139. if(b == nil){
  140. fprint(2, "cannot open disk spool file\n");
  141. wexits("Bopen temp");
  142. }
  143. nargv[0] = fdbuf;
  144. argv = nargv;
  145. }
  146. /* find %!, perhaps after PCL nonsense */
  147. Bseek(b, 0, 0);
  148. psoff = 0;
  149. eol = 0;
  150. for(i=0; i<16; i++){
  151. psoff = Boffset(b);
  152. if(!(p = Brdline(b, eol='\n')) && !(p = Brdline(b, eol='\r'))) {
  153. fprint(2, "cannot find end of first line\n");
  154. wexits("initps");
  155. }
  156. if(p[0]=='\x1B')
  157. p++, psoff++;
  158. if(p[0] == '%' && p[1] == '!')
  159. break;
  160. }
  161. if(i == 16){
  162. werrstr("not ps");
  163. return nil;
  164. }
  165. /* page counting */
  166. npage = 0;
  167. mpage = 16;
  168. page = emalloc(mpage*sizeof(*page));
  169. memset(page, 0, mpage*sizeof(*page));
  170. cantranslate = goodps;
  171. incomments = 1;
  172. Keepreading:
  173. while(p = Brdline(b, eol)) {
  174. if(p[0] == '%')
  175. if(chatty > 1) fprint(2, "ps %.*s\n", utfnlen(p, Blinelen(b)-1), p);
  176. if(npage == mpage) {
  177. mpage *= 2;
  178. page = erealloc(page, mpage*sizeof(*page));
  179. memset(&page[npage], 0, npage*sizeof(*page));
  180. }
  181. if(p[0] != '%' || p[1] != '%')
  182. continue;
  183. if(prefix(p, "%%BeginDocument")) {
  184. nesting++;
  185. continue;
  186. }
  187. if(nesting > 0 && prefix(p, "%%EndDocument")) {
  188. nesting--;
  189. continue;
  190. }
  191. if(nesting)
  192. continue;
  193. if(prefix(p, "%%EndComment")) {
  194. incomments = 0;
  195. continue;
  196. }
  197. if(reverse == -1 && prefix(p, "%%PageOrder")) {
  198. /* glean whether we should reverse the viewing order */
  199. p[Blinelen(b)-1] = 0;
  200. if(strstr(p, "Ascend"))
  201. reverse = 0;
  202. else if(strstr(p, "Descend"))
  203. reverse = 1;
  204. else if(strstr(p, "Special"))
  205. dumb = 1;
  206. p[Blinelen(b)-1] = '\n';
  207. continue;
  208. } else if(prefix(p, "%%Trailer")) {
  209. incomments = 1;
  210. page[npage].offset = Boffset(b)-Blinelen(b);
  211. trailer = 1;
  212. continue;
  213. } else if(incomments && prefix(p, "%%Orientation")) {
  214. if(strstr(p, "Landscape"))
  215. landscape = 1;
  216. } else if(incomments && Dx(bbox)==0 && prefix(p, q="%%BoundingBox")) {
  217. bbox = rdbbox(p+strlen(q)+1);
  218. if(chatty)
  219. /* can't use %R because haven't initdraw() */
  220. fprint(2, "document bbox [%d %d %d %d]\n",
  221. RECT(bbox));
  222. continue;
  223. }
  224. /*
  225. * If they use the initgraphics command, we can't play our translation tricks.
  226. */
  227. p[Blinelen(b)-1] = 0;
  228. if((q=strstr(p, "initgraphics")) && ((r=strchr(p, '%'))==nil || r > q))
  229. cantranslate = 0;
  230. p[Blinelen(b)-1] = eol;
  231. if(!prefix(p, "%%Page:"))
  232. continue;
  233. /*
  234. * figure out of the %%Page: line contains a page number
  235. * or some other page description to use in the menu bar.
  236. *
  237. * lines look like %%Page: x y or %%Page: x
  238. * we prefer just x, and will generate our
  239. * own if necessary.
  240. */
  241. p[Blinelen(b)-1] = 0;
  242. if(chatty) fprint(2, "page %s\n", p);
  243. r = p+7;
  244. while(*r == ' ' || *r == '\t')
  245. r++;
  246. q = r;
  247. while(*q && *q != ' ' && *q != '\t')
  248. q++;
  249. free(page[npage].name);
  250. if(*r) {
  251. if(*r == '"' && *q == '"')
  252. r++, q--;
  253. if(*q)
  254. *q = 0;
  255. page[npage].name = estrdup(r);
  256. *q = 'x';
  257. } else {
  258. snprint(tmp, sizeof tmp, "p %ld", npage+1);
  259. page[npage].name = estrdup(tmp);
  260. }
  261. /*
  262. * store the offset info for later viewing
  263. */
  264. trailer = 0;
  265. p[Blinelen(b)-1] = eol;
  266. page[npage++].offset = Boffset(b)-Blinelen(b);
  267. }
  268. if(Blinelen(b) > 0){
  269. fprint(2, "page: linelen %d\n", Blinelen(b));
  270. Bseek(b, Blinelen(b), 1);
  271. goto Keepreading;
  272. }
  273. if(Dx(bbox) == 0 || Dy(bbox) == 0)
  274. bbox = Rect(0,0,612,792); /* 8½×11 */
  275. /*
  276. * if we didn't find any pages, assume the document
  277. * is one big page
  278. */
  279. if(npage == 0) {
  280. dumb = 1;
  281. if(chatty) fprint(2, "don't know where pages are\n");
  282. reverse = 0;
  283. goodps = 0;
  284. trailer = 0;
  285. page[npage].name = "p 1";
  286. page[npage++].offset = 0;
  287. }
  288. if(npage+2 > mpage) {
  289. mpage += 2;
  290. page = erealloc(page, mpage*sizeof(*page));
  291. memset(&page[mpage-2], 0, 2*sizeof(*page));
  292. }
  293. if(!trailer)
  294. page[npage].offset = Boffset(b);
  295. Bseek(b, 0, 2); /* EOF */
  296. page[npage+1].offset = Boffset(b);
  297. d = emalloc(sizeof(*d));
  298. ps = emalloc(sizeof(*ps));
  299. ps->page = page;
  300. ps->npage = npage;
  301. ps->bbox = bbox;
  302. ps->psoff = psoff;
  303. d->extra = ps;
  304. d->npage = ps->npage;
  305. d->b = b;
  306. d->drawpage = psdrawpage;
  307. d->pagename = pspagename;
  308. d->fwdonly = ps->clueless = dumb;
  309. d->docname = argv[0];
  310. if(spawngs(ps, "-dSAFER") < 0)
  311. return nil;
  312. if(!cantranslate)
  313. bbox.min = ZP;
  314. setdim(ps, bbox, ppi, landscape);
  315. if(goodps){
  316. /*
  317. * We want to only send the page (i.e. not header and trailer) information
  318. * for each page, so initialize the device by sending the header now.
  319. */
  320. pswritepage(d, ps->gsfd, -1);
  321. waitgs(ps);
  322. }
  323. if(dumb) {
  324. fprint(ps->gsfd, "(%s) run\n", argv[0]);
  325. fprint(ps->gsfd, "(/fd/3) (w) file dup (THIS IS NOT A PLAN9 BITMAP 01234567890123456789012345678901234567890123456789\\n) writestring flushfile\n");
  326. }
  327. ps->bbox = bbox;
  328. return d;
  329. }
  330. static int
  331. pswritepage(Document *d, int fd, int page)
  332. {
  333. Biobuf *b = d->b;
  334. PSInfo *ps = d->extra;
  335. int t, n, i;
  336. long begin, end;
  337. char buf[8192];
  338. if(page == -1)
  339. begin = ps->psoff;
  340. else
  341. begin = ps->page[page].offset;
  342. end = ps->page[page+1].offset;
  343. if(chatty) {
  344. fprint(2, "writepage(%d)... from #%ld to #%ld...\n",
  345. page, begin, end);
  346. }
  347. Bseek(b, begin, 0);
  348. t = end-begin;
  349. n = sizeof(buf);
  350. if(n > t) n = t;
  351. while(t > 0 && (i=Bread(b, buf, n)) > 0) {
  352. if(write(fd, buf, i) != i)
  353. return -1;
  354. t -= i;
  355. if(n > t)
  356. n = t;
  357. }
  358. return end-begin;
  359. }
  360. static Image*
  361. psdrawpage(Document *d, int page)
  362. {
  363. PSInfo *ps = d->extra;
  364. Image *im;
  365. if(ps->clueless)
  366. return readimage(display, ps->gsdfd, 0);
  367. waitgs(ps);
  368. if(goodps)
  369. pswritepage(d, ps->gsfd, page);
  370. else {
  371. pswritepage(d, ps->gsfd, -1);
  372. pswritepage(d, ps->gsfd, page);
  373. pswritepage(d, ps->gsfd, d->npage);
  374. }
  375. /*
  376. * If last line terminator is \r, gs will read ahead to check for \n
  377. * so send one to avoid deadlock.
  378. */
  379. write(ps->gsfd, "\n", 1);
  380. im = readimage(display, ps->gsdfd, 0);
  381. if(im == nil) {
  382. fprint(2, "fatal: readimage error %r\n");
  383. wexits("readimage");
  384. }
  385. waitgs(ps);
  386. return im;
  387. }
  388. static char*
  389. pspagename(Document *d, int page)
  390. {
  391. PSInfo *ps = (PSInfo *) d->extra;
  392. return ps->page[page].name;
  393. }