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