1 /*
2 * tkImgPS.c --
3 *
4 * A photo image file handler for postscript files.
5 *
6 */
7
8 /* Author : Jan Nijtmans */
9 /* Date : 7/24/97 */
10
11 #include <string.h>
12 #include <stdlib.h>
13
14 #include "imgInt.h"
15
16 /*
17 * The format record for the PS file format:
18 */
19
20 static int ChanMatchPS _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Channel chan,
21 CONST char *fileName, Tcl_Obj *format, int *widthPtr, int *heightPtr));
22 static int ObjMatchPS _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *dataObj,
23 Tcl_Obj *format, int *widthPtr, int *heightPtr));
24 static int ChanMatchPDF _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Channel chan,
25 CONST char *fileName, Tcl_Obj *format, int *widthPtr, int *heightPtr));
26 static int ObjMatchPDF _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *dataObj,
27 Tcl_Obj *format, int *widthPtr, int *heightPtr));
28 static int ChanReadPS _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Channel chan,
29 CONST char *fileName, Tcl_Obj *format, Tk_PhotoHandle imageHandle,
30 int destX, int destY, int width, int height, int srcX, int srcY));
31 static int ObjReadPS _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *dataObj,
32 Tcl_Obj *format, Tk_PhotoHandle imageHandle,
33 int destX, int destY, int width, int height, int srcX, int srcY));
34 static int ChanReadPDF _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Channel chan,
35 CONST char *fileName, Tcl_Obj *format, Tk_PhotoHandle imageHandle,
36 int destX, int destY, int width, int height, int srcX, int srcY));
37 static int ObjReadPDF _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *dataObj,
38 Tcl_Obj *format, Tk_PhotoHandle imageHandle,
39 int destX, int destY, int width, int height, int srcX, int srcY));
40 static int ChanWritePS _ANSI_ARGS_((Tcl_Interp *interp, CONST char *filename,
41 Tcl_Obj *format, Tk_PhotoImageBlock *blockPtr));
42 static int StringWritePS _ANSI_ARGS_((Tcl_Interp *interp,
43 Tcl_DString *dataPtr, Tcl_Obj *format,
44 Tk_PhotoImageBlock *blockPtr));
45
46 Tk_PhotoImageFormat imgFmtPS = {
47 "postscript", /* name */
48 (Tk_ImageFileMatchProc *) ChanMatchPS, /* fileMatchProc */
49 (Tk_ImageStringMatchProc *) ObjMatchPS, /* stringMatchProc */
50 (Tk_ImageFileReadProc *) ChanReadPS, /* fileReadProc */
51 (Tk_ImageStringReadProc *) ObjReadPS, /* stringReadProc */
52 (Tk_ImageFileWriteProc *) ChanWritePS, /* fileWriteProc */
53 (Tk_ImageStringWriteProc *) StringWritePS, /* stringWriteProc */
54 };
55
56 Tk_PhotoImageFormat imgFmtPDF = {
57 "pdf", /* name */
58 (Tk_ImageFileMatchProc *) ChanMatchPDF, /* fileMatchProc */
59 (Tk_ImageStringMatchProc *) ObjMatchPDF, /* stringMatchProc */
60 (Tk_ImageFileReadProc *) ChanReadPDF, /* fileReadProc */
61 (Tk_ImageStringReadProc *) ObjReadPDF, /* stringReadProc */
62 (Tk_ImageFileWriteProc *) ChanWritePS, /* fileWriteProc */
63 (Tk_ImageStringWriteProc *) StringWritePS, /* stringWriteProc */
64 };
65
66 /*
67 * Prototypes for local procedures defined in this file:
68 */
69
70 static int CommonMatchPS _ANSI_ARGS_((MFile *handle, Tcl_Obj *format,
71 int *widthPtr, int *heightPtr));
72 static int CommonMatchPDF _ANSI_ARGS_((MFile *handle, Tcl_Obj *format,
73 int *widthPtr, int *heightPtr));
74 static int CommonReadPS _ANSI_ARGS_((Tcl_Interp *interp, MFile *handle,
75 Tcl_Obj *format, Tk_PhotoHandle imageHandle, int destX, int destY,
76 int width, int height, int srcX, int srcY));
77 static int CommonWritePS _ANSI_ARGS_((Tcl_Interp *interp, MFile *handle,
78 Tcl_Obj *format, Tk_PhotoImageBlock *blockPtr));
79 static int parseFormat _ANSI_ARGS_((Tcl_Obj *format, int *zoomx,
80 int *zoomy));
81
parseFormat(format,zoomx,zoomy)82 static int parseFormat(format, zoomx, zoomy)
83 Tcl_Obj *format;
84 int *zoomx;
85 int *zoomy;
86 {
87 int objc, i, length, index = 0;
88 Tcl_Obj **objv = NULL;
89 char *p;
90 double zx = 1.0, zy = 1.0;
91
92 if (!format) {
93 *zoomx = (int) (72 * zx + 0.5);
94 *zoomy = (int) (72 * zy + 0.5);
95 }
96
97 if (ImgListObjGetElements((Tcl_Interp*) NULL, format, &objc, &objv) != TCL_OK) {
98 return -1;
99 }
100 for (i=1; i<objc; i++) {
101 p = Tcl_GetStringFromObj(objv[i], &length);
102 if ((p[0] == '-') && ((i+1)<objc)) {
103 if (length < 2) {
104 index = -1; break;
105 }
106 if (!strncmp(p,"-index", length)) {
107 if (Tcl_GetIntFromObj((Tcl_Interp *) NULL, objv[++i], &index) != TCL_OK) {
108 index = -1; break;
109 }
110 } else if (!strncmp(p, "-zoom", length)) {
111 if (Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, objv[++i], &zx) != TCL_OK) {
112 index = -1; break;
113 }
114 if (i > objc) {
115 zy = zx;
116 } else {
117 p = Tcl_GetStringFromObj(objv[i+1], &length);
118 if (p[0] != '-') {
119 if (Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, objv[++i], &zy) != TCL_OK) {
120 index = -1; break;
121 }
122 } else {
123 zy = zx;
124 }
125 }
126 } else {
127 index = -1; break;
128 }
129 } else {
130 if (Tcl_GetIntFromObj((Tcl_Interp *) NULL, objv[++i], &index) != TCL_OK) {
131 index = -1; break;
132 }
133 }
134 }
135 if (!index) {
136 *zoomx = (int) (72 * zx + 0.5);
137 *zoomy = (int) (72 * zy + 0.5);
138 }
139 return index;
140 }
141
ChanMatchPS(interp,chan,fileName,format,widthPtr,heightPtr)142 static int ChanMatchPS(interp, chan, fileName, format, widthPtr, heightPtr)
143 Tcl_Interp *interp;
144 Tcl_Channel chan;
145 CONST char *fileName;
146 Tcl_Obj *format;
147 int *widthPtr, *heightPtr;
148 {
149 MFile handle;
150
151 ImgFixChanMatchProc(&interp, &chan, &fileName, &format, &widthPtr, &heightPtr);
152
153 handle.data = (char *) chan;
154 handle.state = IMG_CHAN;
155
156 return CommonMatchPS(&handle, format, widthPtr, heightPtr);
157 }
158
ObjMatchPS(interp,data,format,widthPtr,heightPtr)159 static int ObjMatchPS(interp, data, format, widthPtr, heightPtr)
160 Tcl_Interp *interp;
161 Tcl_Obj *data;
162 Tcl_Obj *format;
163 int *widthPtr, *heightPtr;
164 {
165 MFile handle;
166
167 ImgFixObjMatchProc(&interp, &data, &format, &widthPtr, &heightPtr);
168
169 handle.data = ImgGetStringFromObj(data, &handle.length);
170 handle.state = IMG_STRING;
171
172 return CommonMatchPS(&handle, format, widthPtr, heightPtr);
173 }
174
CommonMatchPS(handle,format,widthPtr,heightPtr)175 static int CommonMatchPS(handle, format, widthPtr, heightPtr)
176 MFile *handle;
177 Tcl_Obj *format;
178 int *widthPtr, *heightPtr;
179 {
180 unsigned char buf[41];
181
182 if ((ImgRead(handle, (char *) buf, 11) != 11)
183 || (strncmp("%!PS-Adobe-", (char *) buf, 11) != 0)) {
184 return 0;
185 }
186 while (ImgRead(handle,(char *) buf, 1) == 1) {
187 if (buf[0] == '%' &&
188 (ImgRead(handle, (char *) buf, 2) == 2) &&
189 (!memcmp(buf, "%B", 2) &&
190 (ImgRead(handle, (char *) buf, 11) == 11) &&
191 (!memcmp(buf, "oundingBox:", 11)) &&
192 (ImgRead(handle, (char *) buf, 40) == 40))) {
193 int w, h, zoomx, zoomy;
194 char *p = buf;
195 buf[41] = 0;
196 w = - (int) strtoul(p, &p, 0);
197 h = - (int) strtoul(p, &p, 0);
198 w += strtoul(p, &p, 0);
199 h += strtoul(p, &p, 0);
200 if (parseFormat(format, &zoomx, &zoomy) >= 0) {
201 w = (w * zoomx + 36) / 72;
202 h = (h * zoomy + 36) / 72;
203 }
204 if ((w <= 0) || (h <= 0)) return 0;
205 *widthPtr = w;
206 *heightPtr = h;
207 return 1;
208 }
209 }
210 return 0;
211 }
212
ChanReadPS(interp,chan,fileName,format,imageHandle,destX,destY,width,height,srcX,srcY)213 static int ChanReadPS(interp, chan, fileName, format, imageHandle,
214 destX, destY, width, height, srcX, srcY)
215 Tcl_Interp *interp;
216 Tcl_Channel chan;
217 CONST char *fileName;
218 Tcl_Obj *format;
219 Tk_PhotoHandle imageHandle;
220 int destX, destY;
221 int width, height;
222 int srcX, srcY;
223 {
224 MFile handle;
225
226 handle.data = (char *) chan;
227 handle.state = IMG_CHAN;
228
229 return CommonReadPS(interp, &handle, format, imageHandle, destX, destY,
230 width, height, srcX, srcY);
231 }
232
ObjReadPS(interp,data,format,imageHandle,destX,destY,width,height,srcX,srcY)233 static int ObjReadPS(interp, data, format, imageHandle,
234 destX, destY, width, height, srcX, srcY)
235 Tcl_Interp *interp;
236 Tcl_Obj *data;
237 Tcl_Obj *format;
238 Tk_PhotoHandle imageHandle;
239 int destX, destY;
240 int width, height;
241 int srcX, srcY;
242 {
243 MFile handle;
244
245 ImgReadInit(data,'%',&handle);
246
247 return CommonReadPS(interp, &handle, format, imageHandle,
248 destX, destY, width, height, srcX, srcY);
249 }
250
251 typedef struct myblock {
252 Tk_PhotoImageBlock ck;
253 int dummy; /* extra space for offset[3], in case it is not
254 included already in Tk_PhotoImageBlock */
255 } myblock;
256
257 #define block bl.ck
258
259 static int
CommonReadPS(interp,handle,format,imageHandle,destX,destY,width,height,srcX,srcY)260 CommonReadPS(interp, handle, format, imageHandle,
261 destX, destY, width, height, srcX, srcY)
262 Tcl_Interp *interp;
263 MFile *handle;
264 Tcl_Obj *format;
265 Tk_PhotoHandle imageHandle;
266 int destX, destY;
267 int width, height;
268 int srcX, srcY;
269 {
270 #ifndef MAC_TCL
271 char *argv[10];
272 int len, i, j, fileWidth, fileHeight, maxintensity, index;
273 char *p, type;
274 unsigned char buffer[1025], *line = NULL, *line3 = NULL;
275 char zoom[64], papersize[64];
276 Tcl_Channel chan;
277 Tcl_DString dstring;
278 myblock bl;
279 int zoomx, zoomy;
280
281 index = parseFormat(format, &zoomx, &zoomy);
282 if (index < 0) {
283 Tcl_AppendResult(interp, "invalid format: \"",
284 ImgGetStringFromObj(format, NULL), "\"", (char *) NULL);
285 return TCL_ERROR;
286 }
287 sprintf(zoom, "-r%dx%d", zoomx, zoomy);
288
289 len = ImgRead(handle, buffer, 1024);
290 buffer[1024] = 0;
291 p = strstr(buffer,"%%BoundingBox:");
292 fileHeight = height + srcY;
293 if (p) {
294 /* postscript */
295 p += 14;
296 srcX += (strtoul(p, &p, 0) * zoomx + 36) / 72;
297 fileHeight += (strtoul(p, &p, 0) * zoomy + 36) / 72;
298 strtoul(p, &p, 0);
299 srcY -= (strtoul(p, &p, 0) * zoomy + 36) / 72;
300 } else {
301 /* pdf */
302
303 /*
304 * Extract the pixel position of the upper left corner
305 * of the image from the file. How to do that????
306 * For now I just assume A4-size with 72 pixels/inch.
307 */
308 srcX += (0 * zoomx + 36) / 72;
309 srcY -= (792 * zoomy + 36) /72;
310 }
311
312 sprintf(papersize, "-g%dx%d", srcX+width, fileHeight);
313
314 argv[0] = "gs";
315 argv[1] = "-sDEVICE=ppmraw";
316 argv[2] = zoom;
317 argv[3] = papersize;
318 argv[4] = "-q";
319 argv[5] = "-dNOPAUSE";
320 argv[6] = "-sOutputFile=-";
321 argv[7] = "-";
322
323 chan = Tcl_OpenCommandChannel(interp, 8, argv,
324 TCL_STDIN|TCL_STDOUT|TCL_STDERR|TCL_ENFORCE_MODE);
325 if (!chan) {
326 return TCL_ERROR;
327 }
328 if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") != TCL_OK) {
329 return TCL_ERROR;
330 }
331
332 while (len > 0) {
333 Tcl_Write(chan, (char *) buffer, 1024);
334 len = ImgRead(handle, buffer, 1024);
335 }
336 Tcl_Write(chan,"\nquit\n", 6);
337 Tcl_Flush(chan);
338
339 Tcl_DStringInit(&dstring);
340 len = Tcl_Gets(chan, &dstring);
341 p = Tcl_DStringValue(&dstring);
342 type = p[1];
343 if ((p[0] != 'P') || (type < '4') || (type > '6')) {
344 Tcl_AppendResult(interp, "gs error: \"",
345 p, "\"",(char *) NULL);
346 return TCL_ERROR;
347 }
348 do {
349 Tcl_DStringSetLength(&dstring, 0);
350 Tcl_Gets(chan, &dstring);
351 p = Tcl_DStringValue(&dstring);
352 } while (p[0] == '#');
353 fileWidth = strtoul(p, &p, 0);
354 srcY += (fileHeight = strtoul(p, &p, 0));
355
356 if ((srcX + width) > fileWidth) {
357 width = fileWidth - srcX;
358 }
359 if ((srcY + height) > fileHeight) {
360 height = fileHeight - srcY;
361 }
362 if ((width <= 0) || (height <= 0)) {
363 Tcl_Close(interp, chan);
364 Tcl_DStringFree(&dstring);
365 return TCL_OK;
366 }
367 Tk_PhotoExpand(imageHandle, destX + width, destY + height);
368
369 maxintensity = strtoul(p, &p, 0);
370 if ((type != '4') && !maxintensity) {
371 Tcl_DStringSetLength(&dstring, 0);
372 Tcl_Gets(chan, &dstring);
373 p = Tcl_DStringValue(&dstring);
374 maxintensity = strtoul(p, &p, 0);
375 }
376 Tcl_DStringFree(&dstring);
377 line3 = (unsigned char *) ckalloc(3 * fileWidth);
378 block.pixelSize = 1;
379 block.pitch = block.width = width;
380 block.height = 1;
381 block.offset[0] = 0;
382 block.offset[1] = 0;
383 block.offset[2] = 0;
384 block.offset[3] = 0;
385 switch(type) {
386 case '4':
387 i = (fileWidth+7)/8;
388 line = (unsigned char *) ckalloc(i);
389 while (srcY-- > 0) {
390 Tcl_Read(chan,(char *) line, i);
391 }
392 block.pixelPtr = line3;
393 while (height--) {
394 Tcl_Read(chan, (char *) line, i);
395 for (j = 0; j < width; j++) {
396 line3[j] = ((line[(j+srcX)/8]>>(7-(j+srcX)%8) & 1)) ? 0 : 255;
397 }
398 Tk_PhotoPutBlock(imageHandle, &block, destX, destY++, width, 1);
399 }
400 break;
401 case '5':
402 line = (unsigned char *) ckalloc(fileWidth);
403 while (srcY-- > 0) {
404 Tcl_Read(chan, (char *) line, fileWidth);
405 }
406 block.pixelPtr = line + srcX;
407 while (height--) {
408 unsigned char *c = block.pixelPtr;
409 Tcl_Read(chan, (char *) line, fileWidth);
410 if (maxintensity != 255) {
411 for (j = width; j > 0; j--) {
412 *c = (((int)*c) * maxintensity) / 255;
413 c++;
414 }
415 }
416 Tk_PhotoPutBlock(imageHandle, &block, destX, destY++, width, 1);
417 }
418 break;
419 case '6':
420 i = 3 * fileWidth;
421 line = NULL;
422 while (srcY-- > 0) {
423 Tcl_Read(chan, (char *) line3, i);
424 }
425 block.pixelPtr = line3 + (3 * srcX);
426 block.pixelSize = 3;
427 block.offset[1] = 1;
428 block.offset[2] = 2;
429 while (height--) {
430 unsigned char *c = block.pixelPtr;
431 Tcl_Read(chan, (char *) line3, i);
432 if (maxintensity != 255) {
433 for (j = (3 * width - 1); j >= 0; j--) {
434 *c = (((int)*c) * maxintensity) / 255;
435 c++;
436 }
437 }
438 Tk_PhotoPutBlock(imageHandle, &block, destX, destY++, width, 1);
439 }
440 break;
441 }
442 if (line) {
443 ckfree((char *) line);
444 }
445 ckfree((char *) line3);
446 Tcl_Close(interp, chan);
447 Tcl_ResetResult(interp);
448 return TCL_OK;
449 #else
450 Tcl_AppendResult(interp, "Cannot read postscript file: not implemented",
451 (char *) NULL);
452 return TCL_ERROR;
453 #endif
454 }
ChanReadPDF(interp,chan,fileName,format,imageHandle,destX,destY,width,height,srcX,srcY)455 static int ChanReadPDF(interp, chan, fileName, format, imageHandle,
456 destX, destY, width, height, srcX, srcY)
457 Tcl_Interp *interp;
458 Tcl_Channel chan;
459 CONST char *fileName;
460 Tcl_Obj *format;
461 Tk_PhotoHandle imageHandle;
462 int destX, destY;
463 int width, height;
464 int srcX, srcY;
465 {
466 return ChanReadPS(interp, chan, fileName, format, imageHandle, destX, destY,
467 width, height, srcX, srcY);
468 }
469
ObjReadPDF(interp,data,format,imageHandle,destX,destY,width,height,srcX,srcY)470 static int ObjReadPDF(interp, data, format, imageHandle,
471 destX, destY, width, height, srcX, srcY)
472 Tcl_Interp *interp;
473 Tcl_Obj *data;
474 Tcl_Obj *format;
475 Tk_PhotoHandle imageHandle;
476 int destX, destY;
477 int width, height;
478 int srcX, srcY;
479 {
480 return ObjReadPS(interp, data, format, imageHandle,
481 destX, destY, width, height, srcX, srcY);
482 }
483
ChanWritePS(interp,filename,format,blockPtr)484 static int ChanWritePS(interp, filename, format, blockPtr)
485 Tcl_Interp *interp;
486 CONST char *filename;
487 Tcl_Obj *format;
488 Tk_PhotoImageBlock *blockPtr;
489 {
490 Tcl_Channel chan;
491 MFile handle;
492 int result;
493
494 chan = ImgOpenFileChannel(interp, filename, 0644);
495 if (!chan) {
496 return TCL_ERROR;
497 }
498
499 handle.data = (char *) chan;
500 handle.state = IMG_CHAN;
501
502 result = CommonWritePS(interp, &handle, format, blockPtr);
503 if (Tcl_Close(interp, chan) == TCL_ERROR) {
504 return TCL_ERROR;
505 }
506 return result;
507 }
508
StringWritePS(interp,dataPtr,format,blockPtr)509 static int StringWritePS(interp, dataPtr, format, blockPtr)
510 Tcl_Interp *interp;
511 Tcl_DString *dataPtr;
512 Tcl_Obj *format;
513 Tk_PhotoImageBlock *blockPtr;
514 {
515 MFile handle;
516 int result;
517 Tcl_DString data;
518 ImgFixStringWriteProc(&data, &interp, &dataPtr, &format, &blockPtr);
519 ImgWriteInit(dataPtr, &handle);
520 result = CommonWritePS(interp, &handle, format, blockPtr);
521 ImgPutc(IMG_DONE, &handle);
522 if ((result == TCL_OK) && (dataPtr == &data)) {
523 Tcl_DStringResult(interp, dataPtr);
524 }
525 return result;
526 }
527
CommonWritePS(interp,handle,format,blockPtr)528 static int CommonWritePS(interp, handle, format, blockPtr)
529 Tcl_Interp *interp;
530 MFile *handle;
531 Tcl_Obj *format;
532 Tk_PhotoImageBlock *blockPtr;
533 {
534 return TCL_OK;
535 }
536
ChanMatchPDF(interp,chan,fileName,format,widthPtr,heightPtr)537 static int ChanMatchPDF(interp, chan, fileName, format, widthPtr, heightPtr)
538 Tcl_Interp *interp;
539 Tcl_Channel chan;
540 CONST char *fileName;
541 Tcl_Obj *format;
542 int *widthPtr, *heightPtr;
543 {
544 MFile handle;
545
546 ImgFixChanMatchProc(&interp, &chan, &fileName, &format, &widthPtr, &heightPtr);
547
548 handle.data = (char *) chan;
549 handle.state = IMG_CHAN;
550
551 return CommonMatchPDF(&handle, format, widthPtr, heightPtr);
552 }
553
ObjMatchPDF(interp,data,format,widthPtr,heightPtr)554 static int ObjMatchPDF(interp, data, format, widthPtr, heightPtr)
555 Tcl_Interp *interp;
556 Tcl_Obj *data;
557 Tcl_Obj *format;
558 int *widthPtr, *heightPtr;
559 {
560 MFile handle;
561
562 ImgFixObjMatchProc(&interp, &data, &format, &widthPtr, &heightPtr);
563
564 if (!ImgReadInit(data, '%', &handle)) {
565 return 0;
566 }
567
568 return CommonMatchPDF(&handle, format, widthPtr, heightPtr);
569 }
570
CommonMatchPDF(handle,format,widthPtr,heightPtr)571 static int CommonMatchPDF(handle, format, widthPtr, heightPtr)
572 MFile *handle;
573 Tcl_Obj *format;
574 int *widthPtr, *heightPtr;
575 {
576 unsigned char buf[41];
577 int zoomx, zoomy, w, h;
578
579 if ((ImgRead(handle, (char *) buf, 5) != 5)
580 || (strncmp("%PDF-", (char *) buf, 5) != 0)) {
581 return 0;
582 }
583
584 /* Here w and h should be set to the bounding box of the pdf
585 * data. But I don't know how to extract that from the file.
586 * For now I just assume A4-size with 72 pixels/inch. If anyone
587 * has a better idea, please mail to <j.nijtmans@chello.nl>.
588 */
589
590 w = 612/10;
591 h = 792/10;
592
593 if (parseFormat(format, &zoomx, &zoomy) >= 0) {
594 w = (w * zoomx + 36) / 72;
595 h = (h * zoomy + 36) / 72;
596 }
597 if ((w <= 0) || (h <= 0)) return 0;
598 *widthPtr = w;
599 *heightPtr = h;
600 return 1;
601 }
602