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