1 /*
2  *    (c) Copyright 2001  Vilson G�rtner, Uwe Steinmann.
3  *    (c) Copyright 2002-2004  Uwe Steinmann.
4  *    All rights reserved.
5  *
6  *    This library is free software; you can redistribute it and/or
7  *    modify it under the terms of the GNU Lesser General Public
8  *    License as published by the Free Software Foundation; either
9  *    version 2 of the License, or (at your option) any later version.
10  *
11  *    This library is distributed in the hope that it will be useful,
12  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *    Lesser General Public License for more details.
15  *
16  *    You should have received a copy of the GNU Lesser General Public
17  *    License along with this library; if not, write to the
18  *    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  *    Boston, MA 02111-1307, USA.
20  */
21 
22 /* $Id: */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <time.h>
32 #include <math.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #ifndef WIN32
36 #include <strings.h>
37 #include <sys/select.h>
38 #include <unistd.h>
39 #endif
40 #include <ctype.h>
41 
42 #ifdef WIN32
43 #include <windows.h>
44 #include <winbase.h>
45 #endif
46 
47 #include "ps_intern.h"
48 #include "ps_memory.h"
49 #include "ps_error.h"
50 #include "ps_fontenc.h"
51 #include "ps_inputenc.h"
52 
53 #ifdef HAVE_LIBPNG
54 #	include "png.h"
55 #endif
56 
57 #ifdef HAVE_LIBJPEG
58 #	include "jpeglib.h"
59 #endif
60 
61 #ifdef HAVE_LIBGIF
62 #	include "gif_lib.h"
63 #endif
64 
65 #ifdef HAVE_LIBTIFF
66 #	include "tiffio.h"
67 #endif
68 
69 #ifndef DISABLE_BMP
70 #	include "bmp.h"
71 #endif
72 
73 /* ps_writeproc_file() {{{
74  * Default output function
75  */
76 static size_t
ps_writeproc_file(PSDoc * p,void * data,size_t size)77 ps_writeproc_file(PSDoc *p, void *data, size_t size)
78 {
79 	return fwrite(data, 1, size, p->fp);
80 }
81 /* }}} */
82 
83 /* ps_writeproc_buffer() {{{
84  * Default output function for memory
85  */
86 static size_t
ps_writeproc_buffer(PSDoc * p,void * data,size_t size)87 ps_writeproc_buffer(PSDoc *p, void *data, size_t size)
88 {
89 	return str_buffer_write(p, p->sb, data, size);
90 }
91 /* }}} */
92 
93 /* _ps_delete_font() {{{
94  */
_ps_delete_font(PSDoc * psdoc,PSFont * psfont)95 void _ps_delete_font(PSDoc *psdoc, PSFont *psfont) {
96 	if(NULL == psdoc) {
97 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
98 		return;
99 	}
100 	if(psfont == NULL) {
101 		ps_error(psdoc, PS_RuntimeError, _("PSFont is null."));
102 		return;
103 	}
104 	if(psfont->psdoc != psdoc) {
105 		ps_error(psdoc, PS_RuntimeError, _("This PSFont was created for a different document."));
106 		return;
107 	}
108 
109 	/* Free hash table with char info */
110 	if(psfont->metrics->gadobechars) {
111 		ADOBEINFO *ai;
112 		char *p ;
113 		ght_iterator_t iterator;
114 		for(ai = ght_first(psfont->metrics->gadobechars, &iterator, (void **) &p); ai != NULL; ai = ght_next(psfont->metrics->gadobechars, &iterator, (void **) &p)) {
115 			LIG *lig, *ligtmp;
116 			KERN *k, *ktmp;
117 			PCC *np, *nptmp;
118 			lig = ai->ligs;
119 			while(lig != NULL) {
120 				if(lig->succ)
121 					psdoc->free(psdoc, lig->succ);
122 				if(lig->sub)
123 					psdoc->free(psdoc, lig->sub);
124 				ligtmp = lig->next;
125 				psdoc->free(psdoc, lig);
126 				lig = ligtmp;
127 			}
128 			/*
129 			for (lig=ai->ligs; lig; lig = ligtmp) {
130 				if(lig->succ)
131 					psdoc->free(psdoc, lig->succ);
132 				if(lig->sub)
133 					psdoc->free(psdoc, lig->sub);
134 				ligtmp = lig->next;
135 				psdoc->free(psdoc, lig);
136 			}
137 			*/
138 			k = ai->kerns;
139 			while(k != NULL) {
140 				if(k->succ)
141 					psdoc->free(psdoc, k->succ);
142 				ktmp = k->next;
143 				psdoc->free(psdoc, k);
144 				k = ktmp;
145 			}
146 			for (np=ai->pccs; np; np = nptmp) {
147 				if(np->partname)
148 					psdoc->free(psdoc, np->partname);
149 				nptmp = np->next;
150 				psdoc->free(psdoc, np);
151 			}
152 			if(ai->kern_equivs)
153 				psdoc->free(psdoc, ai->kern_equivs);
154 			psdoc->free(psdoc, ai->adobename);
155 			psdoc->free(psdoc, ai);
156 		}
157 		ght_finalize(psfont->metrics->gadobechars);
158 	}
159 
160 	if(psfont->metrics->fontenc) {
161 		ght_finalize(psfont->metrics->fontenc);
162 	}
163 
164 	if(psfont->metrics->fontname) {
165 		psdoc->free(psdoc, psfont->metrics->fontname);
166 	}
167 
168 	if(psfont->metrics->codingscheme) {
169 		psdoc->free(psdoc, psfont->metrics->codingscheme);
170 	}
171 
172 	if(psfont->metrics) {
173 		psdoc->free(psdoc, psfont->metrics);
174 	}
175 
176 	if(psfont->encoding) {
177 		psdoc->free(psdoc, psfont->encoding);
178 	}
179 
180 	/* FIXME: There is much more to free (e.g. the font metrics) than
181 	 * just the font structure.
182 	 */
183 	psdoc->free(psdoc, psfont);
184 }
185 /* }}} */
186 
187 /* _ps_delete_image() {{{
188  */
_ps_delete_image(PSDoc * psdoc,PSImage * psimage)189 void _ps_delete_image(PSDoc *psdoc, PSImage *psimage) {
190 	if(NULL == psdoc) {
191 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
192 		return;
193 	}
194 	if(NULL == psimage) {
195 		ps_error(psdoc, PS_RuntimeError, _("PSImage is null."));
196 		return;
197 	}
198 
199 	if(psimage->type)
200 		psdoc->free(psdoc, psimage->type);
201 	if(psimage->data)
202 		psdoc->free(psdoc, psimage->data);
203 	if(psimage->name)
204 		psdoc->free(psdoc, psimage->name);
205 	if(psimage->palette)
206 		psdoc->free(psdoc, psimage->palette);
207 	psdoc->free(psdoc, psimage);
208 }
209 /* }}} */
210 
211 /* _ps_delete_pattern() {{{
212  */
_ps_delete_pattern(PSDoc * psdoc,PSPattern * pspattern)213 void _ps_delete_pattern(PSDoc *psdoc, PSPattern *pspattern) {
214 	if(NULL == psdoc) {
215 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
216 		return;
217 	}
218 	if(NULL == pspattern) {
219 		ps_error(psdoc, PS_RuntimeError, _("PSPattern is null."));
220 		return;
221 	}
222 
223 	if(pspattern->name)
224 		psdoc->free(psdoc, pspattern->name);
225 	psdoc->free(psdoc, pspattern);
226 }
227 /* }}} */
228 
229 /* _ps_delete_spotcolor() {{{
230  */
_ps_delete_spotcolor(PSDoc * psdoc,PSSpotColor * spotcolor)231 void _ps_delete_spotcolor(PSDoc *psdoc, PSSpotColor *spotcolor) {
232 	if(NULL == psdoc) {
233 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
234 		return;
235 	}
236 	if(NULL == spotcolor) {
237 		ps_error(psdoc, PS_RuntimeError, _("PSSpotColor is null."));
238 		return;
239 	}
240 
241 	if(spotcolor->name)
242 		psdoc->free(psdoc, spotcolor->name);
243 	psdoc->free(psdoc, spotcolor);
244 }
245 /* }}} */
246 
247 /* _ps_delete_shading() {{{
248  */
_ps_delete_shading(PSDoc * psdoc,PSShading * psshading)249 void _ps_delete_shading(PSDoc *psdoc, PSShading *psshading) {
250 	if(NULL == psdoc) {
251 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
252 		return;
253 	}
254 	if(NULL == psshading) {
255 		ps_error(psdoc, PS_RuntimeError, _("PSShading is null."));
256 		return;
257 	}
258 
259 	if(psshading->name)
260 		psdoc->free(psdoc, psshading->name);
261 	psdoc->free(psdoc, psshading);
262 }
263 /* }}} */
264 
265 /* _ps_delete_gstate() {{{
266  */
_ps_delete_gstate(PSDoc * psdoc,PSGState * gstate)267 void _ps_delete_gstate(PSDoc *psdoc, PSGState *gstate) {
268 	if(NULL == psdoc) {
269 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
270 		return;
271 	}
272 	if(NULL == gstate) {
273 		ps_error(psdoc, PS_RuntimeError, _("PSGState is null."));
274 		return;
275 	}
276 
277 	if(gstate->name)
278 		psdoc->free(psdoc, gstate->name);
279 	psdoc->free(psdoc, gstate);
280 }
281 /* }}} */
282 
283 /* _ps_register_font|image|pattern() {{{
284  * Register font, image with PS document
285  * Returns the font, image id which is the index within the
286  * internal font, image
287  * array + 1. Returns 0 in case of an error.
288  */
289 #define PS_REGISTER_RESOURCE(resname, restype) \
290 static int \
291 _ps_register_##resname(PSDoc *p, restype *resname) \
292 { \
293 	int i; \
294  \
295 	i = 0; \
296 	while(i < p->resname##cnt && p->resname##s[i] != NULL) { \
297 		i++; \
298 	} \
299 	if(i >= p->resname##cnt) { \
300 		if(NULL == (p->resname##s = p->realloc(p, p->resname##s, (p->resname##cnt+5) * sizeof(restype *), _("Could not enlarge memory for internal resource array.")))) { \
301 			return 0; \
302 		} \
303 		memset((void *)&p->resname##s[p->resname##cnt], 0, 5*sizeof(restype *)); \
304 		p->resname##cnt += 5; \
305 	} \
306 	p->resname##s[i] = resname; \
307 	return(i+1); \
308 }
309 
310 #define PS_UNREGISTER_RESOURCE(resname, restype) \
311 static void \
312 _ps_unregister_##resname(PSDoc *p, int id) \
313 { \
314 	if(id > p->resname##cnt || id < 1) { \
315 		ps_error(p, PS_Warning, _("Trying to unregister a resource which does not exist.")); \
316 		return; \
317 	} \
318 	if(p->resname##s[id-1] != NULL) { \
319 		_ps_delete_##resname(p, p->resname##s[id-1]); \
320 		p->resname##s[id-1] = NULL; \
321 	} else { \
322 		ps_error(p, PS_Warning, _("Trying to unregister a resource which does not exist.")); \
323 	} \
324 }
325 
326 #define PS_FIND_RESOURCE(resname, restype) \
327 static int \
328 _ps_find_##resname(PSDoc *p, restype *ptr) \
329 { \
330 	int i; \
331  \
332 	i = 0; \
333 	while(i < p->resname##cnt) { \
334 		if(p->resname##s[i] == ptr) \
335 			return(i+1); \
336 		i++; \
337 	} \
338 	return(0); \
339 }
340 
341 #define PS_FIND_RESOURCE_BY_NAME(resname, restype) \
342 static int \
343 _ps_find_##resname##_by_name(PSDoc *p, const char *name) \
344 { \
345 	int i; \
346  \
347 	i = 0; \
348 	while(i < p->resname##cnt) { \
349 		if(p->resname##s[i] && (0 == strcmp(p->resname##s[i]->name, name))) \
350 			return(i+1); \
351 		i++; \
352 	} \
353 	return(0); \
354 }
355 
356 #define PS_GET_RESOURCE(resname, restype) \
357 static restype * \
358 _ps_get_##resname(PSDoc *p, int id) \
359 { \
360 	if(id > p->resname##cnt || id < 1) { \
361 		ps_error(p, PS_Warning, _("Trying to get a resource which does not exist.")); \
362 		return NULL; \
363 	} \
364 	return(p->resname##s[id-1]); \
365 }
366 
PS_REGISTER_RESOURCE(font,PSFont)367 PS_REGISTER_RESOURCE(font, PSFont)
368 PS_UNREGISTER_RESOURCE(font, PSFont)
369 PS_FIND_RESOURCE(font, PSFont)
370 PS_FIND_RESOURCE_BY_NAME(font, PSFont)
371 PS_GET_RESOURCE(font, PSFont)
372 PS_REGISTER_RESOURCE(image, PSImage)
373 PS_UNREGISTER_RESOURCE(image, PSImage)
374 PS_FIND_RESOURCE(image, PSImage)
375 PS_FIND_RESOURCE_BY_NAME(image, PSImage)
376 PS_GET_RESOURCE(image, PSImage)
377 PS_REGISTER_RESOURCE(pattern, PSPattern)
378 PS_UNREGISTER_RESOURCE(pattern, PSPattern)
379 PS_FIND_RESOURCE(pattern, PSPattern)
380 PS_FIND_RESOURCE_BY_NAME(pattern, PSPattern)
381 PS_GET_RESOURCE(pattern, PSPattern)
382 PS_REGISTER_RESOURCE(shading, PSShading)
383 PS_UNREGISTER_RESOURCE(shading, PSShading)
384 PS_FIND_RESOURCE(shading, PSShading)
385 PS_FIND_RESOURCE_BY_NAME(shading, PSShading)
386 PS_GET_RESOURCE(shading, PSShading)
387 PS_REGISTER_RESOURCE(spotcolor, PSSpotColor)
388 PS_UNREGISTER_RESOURCE(spotcolor, PSSpotColor)
389 PS_FIND_RESOURCE(spotcolor, PSSpotColor)
390 PS_FIND_RESOURCE_BY_NAME(spotcolor, PSSpotColor)
391 PS_GET_RESOURCE(spotcolor, PSSpotColor)
392 PS_REGISTER_RESOURCE(gstate, PSGState)
393 PS_UNREGISTER_RESOURCE(gstate, PSGState)
394 PS_FIND_RESOURCE(gstate, PSGState)
395 PS_FIND_RESOURCE_BY_NAME(gstate, PSGState)
396 PS_GET_RESOURCE(gstate, PSGState)
397 /* }}} */
398 
399 /* ps_output_shading_dict() {{{
400  * Outputs a shading dictionary.
401  */
402 static void ps_output_shading_dict(PSDoc *psdoc, PSShading *psshading) {
403 	ps_printf(psdoc, "<<\n");
404 	ps_printf(psdoc, " /ShadingType %d\n", psshading->type);
405 	if(psshading->type == 3)
406 		ps_printf(psdoc, " /Coords [%.2f %.2f %.2f %.2f %.2f %.2f]\n", psshading->x0, psshading->y0, psshading->r0, psshading->x1, psshading->y1, psshading->r1);
407 	else
408 		ps_printf(psdoc, " /Coords [%.2f %.2f %.2f %.2f]\n", psshading->x0, psshading->y0, psshading->x1, psshading->y1);
409 	ps_printf(psdoc, " /Extend [ %s %s ]\n", psshading->extend0 ? "true" : "false", psshading->extend1 ? "true" : "false");
410 	ps_printf(psdoc, " /AntiAlias %s\n", psshading->antialias ? "true" : "false");
411 	switch(psshading->startcolor.colorspace) {
412 		case PS_COLORSPACE_GRAY:
413 			ps_printf(psdoc, " /ColorSpace /DeviceGray\n");
414 			ps_printf(psdoc, " /Function\n");
415 			ps_printf(psdoc, " <<\n");
416 			ps_printf(psdoc, "  /FunctionType 2 /Domain [ 0 1 ]\n");
417 			ps_printf(psdoc, "  /C0 [ %.4f ]\n", psshading->startcolor.c1);
418 			ps_printf(psdoc, "  /C1 [ %.4f ]\n", psshading->endcolor.c1);
419 			ps_printf(psdoc, "  /N %.4f\n", psshading->N);
420 			ps_printf(psdoc, " >>\n");
421 			break;
422 		case PS_COLORSPACE_RGB:
423 			ps_printf(psdoc, " /ColorSpace /DeviceRGB\n");
424 			ps_printf(psdoc, " /Function\n");
425 			ps_printf(psdoc, " <<\n");
426 			ps_printf(psdoc, "  /FunctionType 2 /Domain [ 0 1 ]\n");
427 			ps_printf(psdoc, "  /C0 [ %.4f %.4f %.4f ]\n", psshading->startcolor.c1, psshading->startcolor.c2, psshading->startcolor.c3);
428 			ps_printf(psdoc, "  /C1 [ %.4f %.4f %.4f ]\n", psshading->endcolor.c1, psshading->endcolor.c2, psshading->endcolor.c3);
429 			ps_printf(psdoc, "  /N %.4f\n", psshading->N);
430 			ps_printf(psdoc, " >>\n");
431 			break;
432 		case PS_COLORSPACE_CMYK:
433 			ps_printf(psdoc, " /ColorSpace /DeviceCMYK\n");
434 			ps_printf(psdoc, " /Function\n");
435 			ps_printf(psdoc, " <<\n");
436 			ps_printf(psdoc, "  /FunctionType 2 /Domain [ 0 1 ]\n");
437 			ps_printf(psdoc, "  /C0 [ %.4f %.4f %.4f %.4f ]\n", psshading->startcolor.c1, psshading->startcolor.c2, psshading->startcolor.c3, psshading->startcolor.c4);
438 			ps_printf(psdoc, "  /C1 [ %.4f %.4f %.4f %.4f ]\n", psshading->endcolor.c1, psshading->endcolor.c2, psshading->endcolor.c3, psshading->endcolor.c4);
439 			ps_printf(psdoc, "  /N %.4f\n", psshading->N);
440 			ps_printf(psdoc, " >>\n");
441 			break;
442 		case PS_COLORSPACE_SPOT: {
443 			PSSpotColor *spotcolor;
444 			spotcolor = _ps_get_spotcolor(psdoc, (int) psshading->startcolor.c1);
445 			if(!spotcolor) {
446 				ps_error(psdoc, PS_RuntimeError, _("Could not find spot color."));
447 				return;
448 			}
449 			ps_printf(psdoc, " /ColorSpace ");
450 			ps_printf(psdoc, "[ /Separation (%s)\n", spotcolor->name);
451 			switch(spotcolor->colorspace) {
452 				case PS_COLORSPACE_GRAY:
453 					ps_printf(psdoc, "  /DeviceGray { 1 %f sub mul 1 exch sub }\n", spotcolor->c1);
454 					break;
455 				case PS_COLORSPACE_RGB: {
456 					float max;
457 					max = (spotcolor->c1 > spotcolor->c2) ? spotcolor->c1 : spotcolor->c2;
458 					max = (max > spotcolor->c3) ? max : spotcolor->c3;
459 					ps_printf(psdoc, "  /DeviceRGB { 1 exch sub dup dup %f exch sub %f mul add exch dup dup %f exch sub %f mul add exch dup %f exch sub %f mul add }\n", max, spotcolor->c1, max, spotcolor->c2, max, spotcolor->c3);
460 					break;
461 				}
462 				case PS_COLORSPACE_CMYK:
463 					ps_printf(psdoc, "  /DeviceCMYK { dup %f mul exch dup %f mul exch dup %f mul exch %f mul }\n", spotcolor->c1, spotcolor->c2, spotcolor->c3, spotcolor->c4);
464 					break;
465 			}
466 			ps_printf(psdoc, "   ]\n");
467 			ps_printf(psdoc, " /Function\n");
468 			ps_printf(psdoc, " <<\n");
469 			ps_printf(psdoc, "  /FunctionType 2 /Domain [ 0 1 ]\n");
470 			ps_printf(psdoc, "  /C0 [ %.4f ]\n", psshading->startcolor.c2);
471 			ps_printf(psdoc, "  /C1 [ %.4f ]\n", psshading->endcolor.c2);
472 			ps_printf(psdoc, "  /N %.4f\n", psshading->N);
473 			ps_printf(psdoc, " >>\n");
474 			break;
475 		}
476 	}
477 	ps_printf(psdoc, ">>\n");
478 }
479 /* }}} */
480 
481 /* Start of API functions */
482 
483 /* PS_get_majorversion() {{{
484  * Get major version of library
485  */
486 PSLIB_API int PSLIB_CALL
PS_get_majorversion(void)487 PS_get_majorversion(void) {
488 	return(LIBPS_MAJOR_VERSION);
489 }
490 /* }}} */
491 
492 /* PS_get_minorversion() {{{
493  * Get minor version of library
494  */
495 PSLIB_API int PSLIB_CALL
PS_get_minorversion(void)496 PS_get_minorversion(void) {
497 	return(LIBPS_MINOR_VERSION);
498 }
499 /* }}} */
500 
501 /* PS_get_subminorversion() {{{
502  * Get minor version of library
503  */
504 PSLIB_API int PSLIB_CALL
PS_get_subminorversion(void)505 PS_get_subminorversion(void) {
506 	return(LIBPS_MICRO_VERSION);
507 }
508 /* }}} */
509 
510 /* PS_boot() {{{
511  * Will set text domain, once it works
512  */
513 PSLIB_API void PSLIB_CALL
PS_boot(void)514 PS_boot(void) {
515 #ifdef ENABLE_NLS
516 	/* do not call textdomain in a library
517 	textdomain (GETTEXT_PACKAGE);
518 	*/
519 //	setlocale(LC_MESSAGES, "");
520 //	setlocale(LC_TIME, "");
521 //	setlocale(LC_CTYPE, "");
522 //	setlocale(LC_ALL, "");
523 	bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
524 #endif
525 }
526 /* }}} */
527 
528 /* PS_shutdown() {{{
529  * Counter part to PS_boot()
530  */
531 PSLIB_API void PSLIB_CALL
PS_shutdown(void)532 PS_shutdown(void) {
533 }
534 /* }}} */
535 
536 /* PS_set_info() {{{
537  * Sets several document information will show up as comments in the Header
538  * of the PostScript document
539  */
540 PSLIB_API void PSLIB_CALL
PS_set_info(PSDoc * p,const char * key,const char * val)541 PS_set_info(PSDoc *p, const char *key, const char *val) {
542 	char *val_buf;
543 	char *key_buf;
544 
545 	if(NULL == p) {
546 		ps_error(p, PS_RuntimeError, _("PSDoc is null."));
547 		return;
548 	}
549 	if(!ps_check_scope(p, PS_SCOPE_OBJECT|PS_SCOPE_DOCUMENT)) {
550 		ps_error(p, PS_RuntimeError, _("%s must be called within 'object', or 'document' scope."), __FUNCTION__);
551 		return;
552 	}
553 
554 	if (key == NULL || key[0] == '\0' || val == NULL || val[0] == '\0') {
555 		ps_error(p, PS_Warning, _("Empty key or value in PS_set_info()."));
556 		return;
557 	}
558 	if(p->headerwritten == ps_true) {
559 		ps_error(p, PS_Warning, _("Calling PS_set_info() has no effect because PostScript header is already written."));
560 	}
561 
562 	val_buf = ps_strdup(p, val);
563 	key_buf = ps_strdup(p, key);
564 	if (0 == strcmp(key_buf, "Keywords")) {
565 		p->Keywords = val_buf;
566 	} else if (0 == strcmp(key_buf, "Subject")) {
567 		p->Subject = val_buf;
568 	} else if (0 == strcmp(key_buf, "Title")) {
569 		p->Title = val_buf;
570 	} else if (0 == strcmp(key_buf, "Creator")) {
571 		p->Creator = val_buf;
572 	} else if (0 == strcmp(key_buf, "Author")) {
573 	  p->Author = val_buf;
574 	} else if (0 == strcmp(key_buf, "BoundingBox")) {
575 		if(p->BoundingBox)
576 			p->free(p, p->BoundingBox);
577 	  p->BoundingBox = val_buf;
578 	} else if (0 == strcmp(key_buf, "Orientation")) {
579 	  p->Orientation = val_buf;
580 	}
581 	p->free(p, key_buf);
582 }
583 /* }}} */
584 
585 /* PS_new2() {{{
586  * Create a new PostScript document. Allows to set memory management
587  * functions and error handler.
588  */
589 PSLIB_API PSDoc * PSLIB_CALL
PS_new2(void (* errorhandler)(PSDoc * p,int type,const char * msg,void * data),void * (* allocproc)(PSDoc * p,size_t size,const char * caller),void * (* reallocproc)(PSDoc * p,void * mem,size_t size,const char * caller),void (* freeproc)(PSDoc * p,void * mem),void * opaque)590 PS_new2(void  (*errorhandler)(PSDoc *p, int type, const char *msg, void *data),
591 	void* (*allocproc)(PSDoc *p, size_t size, const char *caller),
592 	void* (*reallocproc)(PSDoc *p, void *mem, size_t size, const char *caller),
593 	void  (*freeproc)(PSDoc *p, void *mem),
594 	void   *opaque) {
595 	PSDoc *p;
596 
597 	if(allocproc == NULL) {
598 		allocproc = _ps_malloc;
599 		reallocproc = _ps_realloc;
600 		freeproc  = _ps_free;
601 	}
602 	if (errorhandler == NULL)
603 		errorhandler = _ps_errorhandler;
604 
605 	if(NULL == (p = (PSDoc *) (* allocproc) (NULL, sizeof(PSDoc), "PS new"))) {
606 		(*errorhandler)(NULL, PS_MemoryError, _("Could not allocate memory for new PS document."), opaque);
607 		return(NULL);
608 	}
609 	memset((void *)p, 0, (size_t) sizeof(PSDoc));
610 
611 	p->errorhandler = errorhandler;
612 	p->user_data = opaque;
613 	p->malloc = allocproc;
614 	p->realloc = reallocproc;
615 	p->free = freeproc;
616 	p->fp = NULL;
617 	p->sb = NULL;
618 	p->copies = 1;
619 	p->warnings = ps_true;
620 	p->inputenc = ps_get_inputencoding("ISO-8859-1"); //&inputencoding;
621 	p->hdict = NULL;
622 	p->hdictfilename = NULL;
623 	p->categories = dlst_init(allocproc, reallocproc, freeproc);
624 	p->parameters = dlst_init(allocproc, reallocproc, freeproc);
625 	p->values = dlst_init(allocproc, reallocproc, freeproc);
626 	p->bookmarks = dlst_init(allocproc, reallocproc, freeproc);
627 	p->lastbookmarkid = 0;
628 	p->bookmarkdict = NULL;
629 	p->bookmarkcnt = 0;
630 	p->CreationDate = NULL;
631 
632 	/* Initialize list of fonts */
633 	p->fontcnt = 5;
634 	if(NULL == (p->fonts = p->malloc(p, p->fontcnt*sizeof(PSFont *), _("Allocate memory for internal Font list of document.")))) {
635 		return(NULL);
636 	}
637 	memset((void *)p->fonts, 0, p->fontcnt*sizeof(PSFont *));
638 
639 	/* Initialize list of images */
640 	p->imagecnt = 5;
641 	if(NULL == (p->images = p->malloc(p, p->imagecnt*sizeof(PSImage *), _("Allocate memory for internal Image list of document.")))) {
642 		return(NULL);
643 	}
644 	memset((void *)p->images, 0, p->imagecnt*sizeof(PSImage *));
645 
646 	/* Initialize list of patterns */
647 	p->patterncnt = 5;
648 	if(NULL == (p->patterns = p->malloc(p, p->patterncnt*sizeof(PSPattern *), _("Allocate memory for internal Pattern list of document.")))) {
649 		return(NULL);
650 	}
651 	memset((void *)p->patterns, 0, p->patterncnt*sizeof(PSPattern *));
652 
653 	/* Initialize list of spot colors */
654 	p->spotcolorcnt = 5;
655 	if(NULL == (p->spotcolors = p->malloc(p, p->spotcolorcnt*sizeof(PSSpotColor *), _("Allocate memory for internal spot color list of document.")))) {
656 		return(NULL);
657 	}
658 	memset((void *)p->spotcolors, 0, p->spotcolorcnt*sizeof(PSSpotColor *));
659 
660 	/* Initialize list of shadings */
661 	p->shadingcnt = 5;
662 	if(NULL == (p->shadings = p->malloc(p, p->shadingcnt*sizeof(PSShading *), _("Allocate memory for internal Shading list of document.")))) {
663 		return(NULL);
664 	}
665 	memset((void *)p->shadings, 0, p->shadingcnt*sizeof(PSShading *));
666 
667 	/* Initialize list of graphic states*/
668 	p->gstatecnt = 5;
669 	if(NULL == (p->gstates = p->malloc(p, p->gstatecnt*sizeof(PSGState *), _("Allocate memory for internal graphic state list of document.")))) {
670 		return(NULL);
671 	}
672 	memset((void *)p->gstates, 0, p->gstatecnt*sizeof(PSGState *));
673 
674 	p->agstate = 0;
675 	p->agstates[0].x = 0.0;
676 	p->agstates[0].y = 0.0;
677 	p->agstates[0].strokecolor.colorspace = PS_COLORSPACE_GRAY;
678 	p->agstates[0].strokecolor.c1 = 0.0;
679 	p->agstates[0].strokecolorinvalid = ps_false;
680 	p->agstates[0].fillcolor.colorspace = PS_COLORSPACE_GRAY;
681 	p->agstates[0].fillcolor.c1 = 0.0;
682 	p->agstates[0].fillcolorinvalid = ps_false;
683 	p->tstate = 0;
684 	p->tstates[0].tx = 0.0;
685 	p->tstates[0].ty = 0.0;
686 	p->tstates[0].cx = 0.0;
687 	p->tstates[0].cy = 0.0;
688 	p->closefp = ps_false;
689 	p->page_open = ps_false;
690 	p->doc_open = ps_false;
691 	p->scopecount = 0;
692 	p->scopes[0] = PS_SCOPE_OBJECT;
693 	p->headerwritten = ps_false;
694 	p->commentswritten = ps_false;
695 	p->beginprologwritten = ps_false;
696 	p->endprologwritten = ps_false;
697 	p->setupwritten = ps_false;
698 	p->border_width = 1.0;
699 	p->border_style = PS_BORDER_SOLID;
700 	p->border_black = 3.0;
701 	p->border_white = 3.0;
702 	p->border_red = 0.0;
703 	p->border_green = 0.0;
704 	p->border_blue = 1.0;
705 	p->textrendering = -1;
706 
707 	return(p);
708 }
709 /* }}} */
710 
711 /* PS_new() {{{
712  * Create a new PostScript Document. Use the internal memory management
713  * functions and error handler.
714  */
715 PSLIB_API PSDoc * PSLIB_CALL
PS_new(void)716 PS_new(void) {
717 	return(PS_new2(NULL, NULL, NULL, NULL, NULL));
718 }
719 /* }}} */
720 
721 /* PS_get_opaque() {{{
722  * Returns the pointer on the user data as it is passed to each call
723  * of the errorhandler.
724  */
725 PSLIB_API void * PSLIB_CALL
PS_get_opaque(PSDoc * psdoc)726 PS_get_opaque(PSDoc *psdoc) {
727 	if(NULL == psdoc) {
728 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
729 		return(NULL);
730 	}
731 	return(psdoc->user_data);
732 }
733 /* }}}  */
734 
735 /* ps_write_ps_comments() {{{ */
ps_write_ps_comments(PSDoc * psdoc)736 static void ps_write_ps_comments(PSDoc *psdoc) {
737 	int i;
738 	time_t ps_calendar_time;
739 	struct tm *ps_local_tm;
740 	if(NULL != (psdoc->CreationDate = psdoc->malloc(psdoc, LINEBUFLEN/2, _("Allocate memory for PS header field 'CreationTime'.")))) {
741 		ps_calendar_time = time(NULL);
742 		if(ps_calendar_time == (time_t)(-1)) {
743 			sprintf(psdoc->CreationDate,"%s","20/3/2001 3:30 AM");
744 		} else {
745 			ps_local_tm = localtime(&ps_calendar_time);
746 			strftime(psdoc->CreationDate, LINEBUFLEN/2, "%d/%m/%Y %I:%M %p", ps_local_tm);
747 		}
748 	} else {
749 		ps_error(psdoc, PS_MemoryError, _("Cannot allocate memory for PS header field 'CreationTime'."));
750 	}
751 
752 	ps_printf(psdoc, "%%!PS-Adobe-3.0\n");
753 	if(psdoc->Creator)
754 		ps_printf(psdoc, "%%%%Creator: %s (%s)\n", psdoc->Creator, "pslib " LIBPS_DOTTED_VERSION);
755 	else
756 		ps_printf(psdoc, "%%%%Creator: %s\n", "pslib " LIBPS_DOTTED_VERSION);
757 	if(psdoc->CreationDate) {
758 		ps_printf(psdoc, "%%%%Creation-Date: %s\n", psdoc->CreationDate);
759 	}
760 	if(psdoc->Title)
761 		ps_printf(psdoc, "%%%%Title: %s\n", psdoc->Title);
762 	if(psdoc->Author)
763 		ps_printf(psdoc, "%%%%Author: %s\n", psdoc->Author);
764 	ps_printf(psdoc, "%%%%PageOrder: Ascend\n");
765 	ps_printf(psdoc, "%%%%Pages: (atend)\n");
766 	if(psdoc->BoundingBox) {
767 		ps_printf(psdoc, "%%%%BoundingBox: %s\n", psdoc->BoundingBox);
768 	} else {
769 		ps_printf(psdoc, "%%%%BoundingBox: (atend)\n");
770 	}
771 	if(psdoc->Orientation)
772 		ps_printf(psdoc, "%%%%Orientation: %s\n", psdoc->Orientation);
773 	else
774 		ps_printf(psdoc, "%%%%Orientation: (atend)\n");
775 	ps_printf(psdoc, "%%%%DocumentProcessColors: Black\n");
776 	ps_printf(psdoc, "%%%%DocumentCustomColors: \n");
777 	for(i=0; i<psdoc->spotcolorcnt; i++) {
778 		PSSpotColor *spotcolor = psdoc->spotcolors[i];
779 		if(NULL != spotcolor)
780 			ps_printf(psdoc, "%%%%+ (%s)\n", spotcolor->name);
781 	}
782 	ps_printf(psdoc, "%%%%CMYKCustomColor: \n");
783 	for(i=0; i<psdoc->spotcolorcnt; i++) {
784 		PSSpotColor *spotcolor = psdoc->spotcolors[i];
785 		if(NULL != spotcolor && spotcolor->colorspace == PS_COLORSPACE_CMYK)
786 			ps_printf(psdoc, "%%%%+ %f %f %f %f (%s)\n", spotcolor->c1, spotcolor->c2, spotcolor->c3, spotcolor->c4, spotcolor->name);
787 	}
788 	ps_printf(psdoc, "%%%%RGBCustomColor: \n");
789 	for(i=0; i<psdoc->spotcolorcnt; i++) {
790 		PSSpotColor *spotcolor = psdoc->spotcolors[i];
791 		if(NULL != spotcolor && spotcolor->colorspace == PS_COLORSPACE_RGB)
792 			ps_printf(psdoc, "%%%%+ %f %f %f (%s)\n", spotcolor->c1, spotcolor->c2, spotcolor->c3, spotcolor->name);
793 	}
794 	ps_printf(psdoc, "%%%%EndComments\n");
795 	psdoc->commentswritten = ps_true;
796 }
797 /* }}} */
798 
799 /* ps_write_ps_beginprolog() {{{ */
ps_write_ps_beginprolog(PSDoc * psdoc)800 static void ps_write_ps_beginprolog(PSDoc *psdoc) {
801 	ps_enter_scope(psdoc, PS_SCOPE_PROLOG);
802 	ps_printf(psdoc, "%%%%BeginProlog\n");
803 	ps_printf(psdoc, "%%%%BeginResource: definicoes\n");
804 	ps_printf(psdoc, "%%%%EndResource\n");
805 	ps_printf(psdoc, "%%%%BeginProcSet: standard\n");
806 
807 	ps_printf(psdoc, "/PslibDict 300 dict def PslibDict begin/N{def}def/B{bind def}N\n");
808 	ps_printf(psdoc, "/TR{translate}N/vsize 11 72 mul N/hsize 8.5 72 mul N/isls false N\n");
809 	ps_printf(psdoc, "/p{show}N/w{0 rmoveto}B/a{moveto}B/l{lineto}B");
810 	ps_printf(psdoc, "/qs{currentpoint\n");
811 	ps_printf(psdoc, "currentpoint newpath moveto 3 2 roll dup true charpath stroke\n");
812 	ps_printf(psdoc, "stringwidth pop 3 2 roll add exch moveto}B");
813 	ps_printf(psdoc, "/qf{currentpoint\n");
814 	ps_printf(psdoc, "currentpoint newpath moveto 3 2 roll dup true charpath fill\n");
815 	ps_printf(psdoc, "stringwidth pop 3 2 roll add exch moveto}B");
816 	ps_printf(psdoc, "/qsf{currentpoint\n");
817 	ps_printf(psdoc, "currentpoint newpath moveto 3 2 roll dup true charpath gsave stroke grestore fill\n");
818 	ps_printf(psdoc, "stringwidth pop 3 2 roll add exch moveto}B");
819 	ps_printf(psdoc, "/qc{currentpoint\n");
820 	ps_printf(psdoc, "currentpoint newpath moveto 3 2 roll dup true charpath clip\n");
821 	ps_printf(psdoc, "stringwidth pop 3 2 roll add exch moveto}B");
822 	ps_printf(psdoc, "/qsc{currentpoint\n");
823 	ps_printf(psdoc, "currentpoint initclip newpath moveto 3 2 roll dup true charpath clip stroke\n");
824 	ps_printf(psdoc, "stringwidth pop 3 2 roll add exch moveto}B");
825 	ps_printf(psdoc, "/qfc{currentpoint\n");
826 	ps_printf(psdoc, "currentpoint initclip newpath moveto 3 2 roll dup true charpath clip fill\n");
827 	ps_printf(psdoc, "stringwidth pop 3 2 roll add exch moveto}B");
828 	ps_printf(psdoc, "/qfsc{currentpoint\n");
829 	ps_printf(psdoc, "currentpoint initclip newpath moveto 3 2 roll dup true charpath gsave stroke grestore clip fill\n");
830 	ps_printf(psdoc, "stringwidth pop 3 2 roll add exch moveto}B");
831 	ps_printf(psdoc, "/qi{currentpoint\n");
832 	ps_printf(psdoc, "3 2 roll\n");
833 	ps_printf(psdoc, "stringwidth pop 3 2 roll add exch moveto}B");
834 	ps_printf(psdoc, "/tr{currentpoint currentpoint 5 4 roll add moveto}B");
835 	ps_printf(psdoc, "/rt{moveto}B");
836 	ps_printf(psdoc, "/#copies{1}B\n");
837 	ps_printf(psdoc, "/PslibPageBeginHook{pop pop pop pop pop}B\n");
838 	ps_printf(psdoc, "/PslibPageEndHook{pop}B\n");
839 	ps_printf(psdoc, "\n");
840 
841 	ps_printf(psdoc, "/reencdict 12 dict def /ReEncode { reencdict begin\n");
842 	ps_printf(psdoc, "/newcodesandnames exch def /newfontname exch def /basefontname exch def\n");
843 	ps_printf(psdoc, "/basefontdict basefontname findfont def /newfont basefontdict maxlength dict def\n");
844 	ps_printf(psdoc, "basefontdict { exch dup /FID ne { dup /Encoding eq\n");
845 	ps_printf(psdoc, "{ exch dup length array copy newfont 3 1 roll put }\n");
846 	ps_printf(psdoc, "{ exch newfont 3 1 roll put } ifelse } { pop pop } ifelse } forall\n");
847 	ps_printf(psdoc, "newfont /FontName newfontname put newcodesandnames aload pop\n");
848 	ps_printf(psdoc, "128 1 255 { newfont /Encoding get exch /.notdef put } for\n");
849 	ps_printf(psdoc, "newcodesandnames length 2 idiv { newfont /Encoding get 3 1 roll put } repeat\n");
850 	ps_printf(psdoc, "newfontname newfont definefont pop end } def\n");
851 	ps_printf(psdoc, "end\n");
852 	ps_printf(psdoc, "%%%%EndProcSet\n");
853 	ps_printf(psdoc, "%%%%BeginProcSet: colorsep\n");
854 	ps_printf(psdoc, "%%!\n");
855 	ps_printf(psdoc, "%% Colour separation.\n");
856 	ps_printf(psdoc, "%% Ask dvips to do 4 pages. In bop-hook, cycle\n");
857 	ps_printf(psdoc, "%% round CMYK color spaces.\n");
858 	ps_printf(psdoc, "%%\n");
859 	ps_printf(psdoc, "%% Sebastian Rahtz 30.9.93\n");
860 	ps_printf(psdoc, "%% checked 7.1.94\n");
861 	ps_printf(psdoc, "%% from Green Book, and Kunkel Graphic Design with PostScript\n");
862 	ps_printf(psdoc, "%% (Green Book Listing 9-5, on page 153.)\n");
863 	ps_printf(psdoc, "%%\n");
864 	ps_printf(psdoc, "%% This work is placed in the public domain\n");
865 	ps_printf(psdoc, "/seppages  0  def \n");
866 	ps_printf(psdoc, "userdict begin\n");
867 	ps_printf(psdoc, "/Min {%% 3 items on stack\n");
868 	ps_printf(psdoc, "2 copy lt { pop }{ exch pop } ifelse\n");
869 	ps_printf(psdoc, "2 copy lt { pop }{ exch pop } ifelse\n");
870 	ps_printf(psdoc, "} def\n");
871 	ps_printf(psdoc, "/SetGray {\n");
872 	ps_printf(psdoc, " 1 exch sub systemdict begin adjustdot setgray end	\n");
873 	ps_printf(psdoc, "} def\n");
874 	ps_printf(psdoc, "/sethsbcolor {systemdict begin\n");
875 	ps_printf(psdoc, "  sethsbcolor currentrgbcolor end\n");
876 	ps_printf(psdoc, "  userdict begin setrgbcolor end}def \n");
877 	ps_printf(psdoc, "\n");
878 	ps_printf(psdoc, "/ToCMYK\n");
879 	ps_printf(psdoc, "%% Red book p 305\n");
880 	ps_printf(psdoc, "  {\n");
881 	ps_printf(psdoc, "%% subtract each colour from 1\n");
882 	ps_printf(psdoc, "  3 { 1 exch sub 3 1 roll } repeat\n");
883 	ps_printf(psdoc, "%% define percent of black undercolor\n");
884 	ps_printf(psdoc, "%% find minimum (k)\n");
885 	ps_printf(psdoc, "  3 copy  Min \n");
886 	ps_printf(psdoc, "%% remove undercolor\n");
887 	ps_printf(psdoc, "  blackUCR sub\n");
888 	ps_printf(psdoc, "  dup 0 lt {pop 0} if \n");
889 	ps_printf(psdoc, "  /percent_UCR exch def \n");
890 	ps_printf(psdoc, "%%\n");
891 	ps_printf(psdoc, "%% subtract that from each colour\n");
892 	ps_printf(psdoc, "%%\n");
893 	ps_printf(psdoc, "  3 { percent_UCR sub 3 1 roll } repeat \n");
894 	ps_printf(psdoc, "%% work out black itself\n");
895 	ps_printf(psdoc, "  percent_UCR 1.25 mul %% 1 exch sub\n");
896 	ps_printf(psdoc, "%% stack should now have C M Y K\n");
897 	ps_printf(psdoc, "} def \n");
898 	ps_printf(psdoc, "%%\n");
899 	ps_printf(psdoc, "%% crop marks\n");
900 	ps_printf(psdoc, "%%\n");
901 	ps_printf(psdoc, "/cX 18 def \n");
902 	ps_printf(psdoc, "/CM{gsave TR 0 cX neg moveto 0 cX lineto stroke\n");
903 	ps_printf(psdoc, "cX neg 0 moveto cX 0 lineto stroke grestore}def \n");
904 	ps_printf(psdoc, "%%\n");
905 	ps_printf(psdoc, "/bop-hook{cX dup TR\n");
906 	ps_printf(psdoc, "%%\n");
907 	ps_printf(psdoc, "%% which page are we producing\n");
908 	ps_printf(psdoc, "%%\n");
909 	ps_printf(psdoc, "   seppages 1 add \n");
910 	ps_printf(psdoc, "    /seppages exch def\n");
911 	ps_printf(psdoc, "     seppages 5 eq { /seppages  1  def } if\n");
912 	ps_printf(psdoc, "     seppages 1 eq { \n");
913 	ps_printf(psdoc, "      /ColourName (CYAN) def \n");
914 	ps_printf(psdoc, "      CYAN setupcolor    \n");
915 	ps_printf(psdoc, "      /WhichColour 3 def } if \n");
916 	ps_printf(psdoc, "   seppages 2 eq { \n");
917 	ps_printf(psdoc, "      /ColourName (MAGENTA) def \n");
918 	ps_printf(psdoc, "      MAGENTA setupcolor \n");
919 	ps_printf(psdoc, "     /WhichColour 2 def } if\n");
920 	ps_printf(psdoc, "   seppages 3 eq { \n");
921 	ps_printf(psdoc, "      /ColourName (YELLOW) def\n");
922 	ps_printf(psdoc, "      YELLOW setupcolor  \n");
923 	ps_printf(psdoc, "      /WhichColour 1 def } if \n");
924 	ps_printf(psdoc, "   seppages 4 eq { \n");
925 	ps_printf(psdoc, "      /ColourName (BLACK) def \n");
926 	ps_printf(psdoc, "      BLACK setupcolor   \n");
927 	ps_printf(psdoc, "      /WhichColour 0 def } if \n");
928 	ps_printf(psdoc, "%%\n");
929 	ps_printf(psdoc, "%% crop marks\n");
930 	ps_printf(psdoc, "%%\n");
931 	ps_printf(psdoc, "gsave .3 setlinewidth \n");
932 	ps_printf(psdoc, "3 -7 moveto\n");
933 	ps_printf(psdoc, "/Helvetica findfont 6 scalefont setfont\n");
934 	ps_printf(psdoc, "ColourName show\n");
935 	ps_printf(psdoc, "0 0 CM \n");
936 	ps_printf(psdoc, "vsize cX 2 mul sub dup hsize cX 2 mul sub dup isls{4 2 roll}if 0 CM \n");
937 	ps_printf(psdoc, "exch CM 0 \n");
938 	ps_printf(psdoc, "exch CM \n");
939 	ps_printf(psdoc, "grestore 0 cX -2 mul TR isls\n");
940 	ps_printf(psdoc, "{cX -2 mul 0 TR}if\n");
941 	ps_printf(psdoc, "	  } def end\n");
942 	ps_printf(psdoc, "%% \n");
943 	ps_printf(psdoc, "/separations 48 dict def\n");
944 	ps_printf(psdoc, "separations begin\n");
945 	ps_printf(psdoc, "   /cmykprocs [ %%def\n");
946 	ps_printf(psdoc, "       %% cyan\n");
947 	ps_printf(psdoc, "    { pop pop  pop SetGray  }\n");
948 	ps_printf(psdoc, "       %% magenta\n");
949 	ps_printf(psdoc, "    { pop pop exch pop SetGray  }\n");
950 	ps_printf(psdoc, "       %% yellow\n");
951 	ps_printf(psdoc, "    { pop 3 1 roll pop pop SetGray  }\n");
952 	ps_printf(psdoc, "       %% black\n");
953 	ps_printf(psdoc, "    { 4 1 roll pop pop pop SetGray  }\n");
954 	ps_printf(psdoc, "   ] def\n");
955 	ps_printf(psdoc, "   /rgbprocs [ %%def\n");
956 	ps_printf(psdoc, "       %% cyan\n");
957 	ps_printf(psdoc, "    { ToCMYK pop pop pop SetGray }\n");
958 	ps_printf(psdoc, "       %% magenta\n");
959 	ps_printf(psdoc, "    { ToCMYK pop pop exch pop SetGray }\n");
960 	ps_printf(psdoc, "       %% yellow\n");
961 	ps_printf(psdoc, "    { ToCMYK pop 3 1 roll pop pop SetGray }\n");
962 	ps_printf(psdoc, "       %% black\n");
963 	ps_printf(psdoc, "    { ToCMYK 4 1 roll pop pop pop SetGray  }\n");
964 	ps_printf(psdoc, "   ] def\n");
965 	ps_printf(psdoc, "   /testprocs [ %%def\n");
966 	ps_printf(psdoc, "       %% cyan\n");
967 	ps_printf(psdoc, "    { ToCMYK pop pop pop  }\n");
968 	ps_printf(psdoc, "       %% magenta\n");
969 	ps_printf(psdoc, "    { ToCMYK pop pop exch pop  }\n");
970 	ps_printf(psdoc, "       %% yellow\n");
971 	ps_printf(psdoc, "    { ToCMYK pop 3 1 roll pop pop  }\n");
972 	ps_printf(psdoc, "       %% black\n");
973 	ps_printf(psdoc, "    { ToCMYK 4 1 roll pop pop pop   }\n");
974 	ps_printf(psdoc, "   ] def\n");
975 	ps_printf(psdoc, "   /screenangles [ %%def\n");
976 	ps_printf(psdoc, "       105  %% cyan\n");
977 	ps_printf(psdoc, "       75    %% magenta\n");
978 	ps_printf(psdoc, "       0      %% yellow\n");
979 	ps_printf(psdoc, "       45    %% black\n");
980 	ps_printf(psdoc, "   ] def\n");
981 	ps_printf(psdoc, "end  %% separations\n");
982 	ps_printf(psdoc, "\n");
983 	ps_printf(psdoc, "%% setupcolortakes 0, 1, 2, or 3 as its argument,\n");
984 	ps_printf(psdoc, "%% for cyan, magenta, yellow, and black.\n");
985 	ps_printf(psdoc, "/CYAN 0 def           /MAGENTA 1 def\n");
986 	ps_printf(psdoc, "/YELLOW 2 def         /BLACK 3 def\n");
987 	ps_printf(psdoc, "/setupcolor{ %%def\n");
988 	ps_printf(psdoc, "   userdict begin\n");
989 	ps_printf(psdoc, "       dup separations /cmykprocs get exch get\n");
990 	ps_printf(psdoc, "       /setcmykcolor exch def\n");
991 	ps_printf(psdoc, "       dup separations /rgbprocs get exch get\n");
992 	ps_printf(psdoc, "       /setrgbcolor exch def\n");
993 	ps_printf(psdoc, "       dup separations /testprocs get exch get\n");
994 	ps_printf(psdoc, "       /testrgbcolor exch def\n");
995 	ps_printf(psdoc, "       separations /screenangles get exch get\n");
996 	ps_printf(psdoc, "       currentscreen\n");
997 	ps_printf(psdoc, "           exch pop 3 -1 roll exch\n");
998 	ps_printf(psdoc, "       setscreen\n");
999 	ps_printf(psdoc, "       /setscreen { pop pop pop } def\n");
1000 	ps_printf(psdoc, "%%\n");
1001 	ps_printf(psdoc, "%% redefine setgray so that it only shows on the black separation\n");
1002 	ps_printf(psdoc, "%%\n");
1003 	ps_printf(psdoc, "      /setgray {\n");
1004 	ps_printf(psdoc, "       WhichColour 0 eq\n");
1005 	ps_printf(psdoc, "       {systemdict begin adjustdot setgray end} \n");
1006 	ps_printf(psdoc, "       {pop systemdict begin 1 setgray end}\n");
1007 	ps_printf(psdoc, "       ifelse}def \n");
1008 	ps_printf(psdoc, "   end\n");
1009 	ps_printf(psdoc, "} bind def\n");
1010 	ps_printf(psdoc, "\n");
1011 	ps_printf(psdoc, "%%\n");
1012 	ps_printf(psdoc, "%% from Kunkel\n");
1013 	ps_printf(psdoc, "%%\n");
1014 	ps_printf(psdoc, "/adjustdot { dup 0 eq { } { dup 1 exch sub .1 mul add} ifelse } def\n");
1015 	ps_printf(psdoc, "%%\n");
1016 	ps_printf(psdoc, "%% redefine existing operators\n");
1017 	ps_printf(psdoc, "%%\n");
1018 	ps_printf(psdoc, "%% Percent of undercolor removal\n");
1019 	ps_printf(psdoc, "/magentaUCR .3 def  \n");
1020 	ps_printf(psdoc, "/yellowUCR .07 def  \n");
1021 	ps_printf(psdoc, "/blackUCR .4 def \n");
1022 	ps_printf(psdoc, "%%\n");
1023 	ps_printf(psdoc, "%% Correct yellow and magenta\n");
1024 	ps_printf(psdoc, "/correctMY {rgb2cym\n");
1025 	ps_printf(psdoc, "  1 index yellowUCR mul sub 3 1 roll\n");
1026 	ps_printf(psdoc, "  1 index magentaUCR mul sub 3 1 roll\n");
1027 	ps_printf(psdoc, "  3 1 roll rgb2cym}def\n");
1028 	ps_printf(psdoc, "%% \n");
1029 	ps_printf(psdoc, "%%(bluely green ) =\n");
1030 	ps_printf(psdoc, "%%CYAN setupcolor\n");
1031 	ps_printf(psdoc, "%%.1 .4 .5  testrgbcolor =\n");
1032 	ps_printf(psdoc, "%%MAGENTA setupcolor\n");
1033 	ps_printf(psdoc, "%%.1 .4 .5  testrgbcolor =\n");
1034 	ps_printf(psdoc, "%%YELLOW setupcolor\n");
1035 	ps_printf(psdoc, "%%.1 .4 .5  testrgbcolor =\n");
1036 	ps_printf(psdoc, "%%BLACK setupcolor\n");
1037 	ps_printf(psdoc, "%%.1 .4 .5  testrgbcolor =\n");
1038 	ps_printf(psdoc, "%%quit\n");
1039 	ps_printf(psdoc, "%%%%EndProcSet\n");
1040 
1041 	/* The fontenc vector is placed outside the PslibDict dictionary */
1042 	{
1043 	int i, j;
1044 	ENCODING *fontenc;
1045 	fontenc = &fontencoding[0];
1046 	ps_printf(psdoc, "/fontenc-%s [\n", fontenc->name);
1047 	for(i=0; i<32; i++) {
1048 		for(j=0; j<8; j++) {
1049 			if((fontenc->vec[i*8+j] != NULL) && (*(fontenc->vec[i*8+j]) != '\0'))
1050 				ps_printf(psdoc, "8#%03o /%s ", i*8+j, fontenc->vec[i*8+j]);
1051 		}
1052 		ps_printf(psdoc, "\n");
1053 	}
1054 	ps_printf(psdoc, "] def\n");
1055 	}
1056 
1057 	ps_printf(psdoc, "/pdfmark where {pop} {userdict /pdfmark /cleartomark load put} ifelse\n");
1058 	if(psdoc->Creator)
1059 		ps_printf(psdoc, "[ /Creator (%s \\(%s\\))\n", psdoc->Creator, "pslib " LIBPS_DOTTED_VERSION);
1060 	else
1061 		ps_printf(psdoc, "[ /Creator (%s)\n", "pslib " LIBPS_DOTTED_VERSION);
1062 	if(psdoc->CreationDate) {
1063 		ps_printf(psdoc, "  /Creation-Date (%s)\n", psdoc->CreationDate);
1064 	}
1065 	if(psdoc->Title)
1066 		ps_printf(psdoc, "  /Title (%s)\n", psdoc->Title);
1067 	if(psdoc->Author)
1068 		ps_printf(psdoc, "  /Author (%s)\n", psdoc->Author);
1069 	if(psdoc->Keywords)
1070 		ps_printf(psdoc, "  /Keywords (%s)\n", psdoc->Keywords);
1071 	if(psdoc->Subject)
1072 		ps_printf(psdoc, "  /Subject (%s)\n", psdoc->Subject);
1073 	ps_printf(psdoc, "/DOCINFO pdfmark\n");
1074 	psdoc->beginprologwritten = ps_true;
1075 }
1076 /* }}} */
1077 
1078 /* ps_write_ps_endprolog() {{{ */
ps_write_ps_endprolog(PSDoc * psdoc)1079 static void ps_write_ps_endprolog(PSDoc *psdoc) {
1080 	ps_printf(psdoc, "%%%%EndProlog\n");
1081 	ps_leave_scope(psdoc, PS_SCOPE_PROLOG);
1082 	psdoc->endprologwritten = ps_true;
1083 }
1084 /* }}} */
1085 
1086 /* ps_write_ps_setup() {{{ */
ps_write_ps_setup(PSDoc * psdoc)1087 static void ps_write_ps_setup(PSDoc *psdoc) {
1088 	ps_printf(psdoc, "%%%%BeginSetup\n");
1089 	ps_printf(psdoc, "PslibDict begin\n");
1090 	if(psdoc->copies > 1)
1091 		ps_printf(psdoc, "/#copies %d def\n", psdoc->copies);
1092 	ps_printf(psdoc, "%%%%EndSetup\n");
1093 	psdoc->setupwritten = ps_true;
1094 }
1095 /* }}} */
1096 
1097 /* ps_write_ps_header() {{{
1098  * Write the PostScript header
1099  */
ps_write_ps_header(PSDoc * psdoc)1100 static void ps_write_ps_header(PSDoc *psdoc) {
1101 	if(psdoc->headerwritten == ps_true)
1102 		return;
1103 	if(psdoc->commentswritten == ps_false)
1104 		ps_write_ps_comments(psdoc);
1105 	if(psdoc->beginprologwritten == ps_false)
1106 		ps_write_ps_beginprolog(psdoc);
1107 	if(psdoc->endprologwritten == ps_false)
1108 		ps_write_ps_endprolog(psdoc);
1109 	if(psdoc->setupwritten == ps_false)
1110 		ps_write_ps_setup(psdoc);
1111 	psdoc->headerwritten = ps_true;
1112 }
1113 /* }}} */
1114 
1115 /* ps_setcolor() {{{
1116  * Outputs PostScript commands to set the color, but checks before
1117  * if it was already set. whichcolor is either PS_COLORTYPE_FILL
1118  * or PS_COLORTYPE_STROKE. The function only outputs something if
1119  * [fill|stroke]colorinvalid is set. [fill|stroke]colorinvalid is set
1120  * when the color is set by PS_setcolor() or this function has set
1121  * the fill or stroke color.
1122  */
ps_setcolor(PSDoc * psdoc,int whichcolor)1123 static void ps_setcolor(PSDoc *psdoc, int whichcolor) {
1124 	PSColor *currentcolor = NULL;
1125 
1126 	if(ps_check_scope(psdoc, PS_SCOPE_PATTERN)) {
1127 		if(psdoc->pattern->painttype == 2) {
1128 			ps_error(psdoc, PS_Warning, _("Setting color inside a pattern of PaintType 2 is not allowed."), __FUNCTION__);
1129 			return;
1130 		}
1131 	}
1132 
1133 	switch(whichcolor) {
1134 		case PS_COLORTYPE_FILL:
1135 			if(psdoc->agstates[psdoc->agstate].fillcolorinvalid) {
1136 				psdoc->agstates[psdoc->agstate].strokecolorinvalid = ps_true;
1137 				psdoc->agstates[psdoc->agstate].fillcolorinvalid = ps_false;
1138 				currentcolor = &(psdoc->agstates[psdoc->agstate].fillcolor);
1139 			}
1140 			break;
1141 		case PS_COLORTYPE_STROKE:
1142 			if(psdoc->agstates[psdoc->agstate].strokecolorinvalid) {
1143 				psdoc->agstates[psdoc->agstate].fillcolorinvalid = ps_true;
1144 				psdoc->agstates[psdoc->agstate].strokecolorinvalid = ps_false;
1145 				currentcolor = &(psdoc->agstates[psdoc->agstate].strokecolor);
1146 			}
1147 			break;
1148 	}
1149 	if(currentcolor) {
1150 		switch(currentcolor->colorspace) {
1151 			case PS_COLORSPACE_GRAY:
1152 				ps_printf(psdoc, "%f setgray\n", currentcolor->c1);
1153 				break;
1154 			case PS_COLORSPACE_RGB:
1155 				ps_printf(psdoc, "%.4f %.4f %.4f setrgbcolor\n", currentcolor->c1, currentcolor->c2, currentcolor->c3);
1156 				break;
1157 			case PS_COLORSPACE_CMYK:
1158 				ps_printf(psdoc, "%.4f %.4f %.4f %.4f setcmykcolor\n", currentcolor->c1, currentcolor->c2, currentcolor->c3, currentcolor->c4);
1159 				break;
1160 			case PS_COLORSPACE_PATTERN: {
1161 				PSPattern *pspattern = _ps_get_pattern(psdoc, (int) currentcolor->pattern);
1162 				if(NULL == pspattern) {
1163 					ps_error(psdoc, PS_RuntimeError, _("PSPattern is null."));
1164 					return;
1165 				}
1166 				if(pspattern->painttype == 1) {
1167 					ps_printf(psdoc, "%s setpattern\n", pspattern->name);
1168 				} else {
1169 					ps_printf(psdoc, "[/Pattern [/");
1170 					switch(currentcolor->prevcolorspace) {
1171 						case PS_COLORSPACE_GRAY:
1172 							ps_printf(psdoc, "DeviceGray]] setcolorspace\n");
1173 							ps_printf(psdoc, "%.4f %s setcolor\n", currentcolor->c1, pspattern->name);
1174 							break;
1175 						case PS_COLORSPACE_CMYK:
1176 							ps_printf(psdoc, "DeviceCMYK]] setcolorspace\n");
1177 							ps_printf(psdoc, "%.4f %.4f %.4f %.4f %s setcolor\n", currentcolor->c1, currentcolor->c2, currentcolor->c3, currentcolor->c4, pspattern->name);
1178 							break;
1179 						case PS_COLORSPACE_RGB:
1180 							ps_printf(psdoc, "DeviceRGB]] setcolorspace\n");
1181 							ps_printf(psdoc, "%.4f %.4f %.4f %s setcolor\n", currentcolor->c1, currentcolor->c2, currentcolor->c3, pspattern->name);
1182 							break;
1183 						case PS_COLORSPACE_SPOT: {
1184 							PSSpotColor *spotcolor;
1185 							spotcolor = _ps_get_spotcolor(psdoc, (int) currentcolor->c1);
1186 							if(!spotcolor) {
1187 								ps_error(psdoc, PS_RuntimeError, _("Could not find spot color."));
1188 								return;
1189 							}
1190 							ps_printf(psdoc, "Separation (%s)\n", spotcolor->name);
1191 							switch(spotcolor->colorspace) {
1192 								case PS_COLORSPACE_GRAY:
1193 									ps_printf(psdoc, "  /DeviceGray { 1 %f sub mul 1 exch sub }\n", spotcolor->c1);
1194 									break;
1195 								case PS_COLORSPACE_RGB: {
1196 									float max;
1197 									max = (spotcolor->c1 > spotcolor->c2) ? spotcolor->c1 : spotcolor->c2;
1198 									max = (max > spotcolor->c3) ? max : spotcolor->c3;
1199 									ps_printf(psdoc, "  /DeviceRGB { 1 exch sub dup dup %f exch sub %f mul add exch dup dup %f exch sub %f mul add exch dup %f exch sub %f mul add }\n", max, spotcolor->c1, max, spotcolor->c2, max, spotcolor->c3);
1200 									break;
1201 								}
1202 								case PS_COLORSPACE_CMYK:
1203 									ps_printf(psdoc, "  /DeviceCMYK { dup %f mul exch dup %f mul exch dup %f mul exch %f mul }\n", spotcolor->c1, spotcolor->c2, spotcolor->c3, spotcolor->c4);
1204 									break;
1205 							}
1206 							ps_printf(psdoc, "]] setcolorspace\n");
1207 							break;
1208 						}
1209 						default:
1210 							ps_error(psdoc, PS_Warning, _("Current stroke/fill color is not RGB, CMYK, Gray or spot while setting a pattern of paint type 1."));
1211 					}
1212 				}
1213 				break;
1214 			}
1215 			case PS_COLORSPACE_SPOT: {
1216 				PSSpotColor *spotcolor;
1217 				spotcolor = _ps_get_spotcolor(psdoc, (int) currentcolor->c1);
1218 				if(!spotcolor) {
1219 					ps_error(psdoc, PS_RuntimeError, _("Could not find spot color."));
1220 					return;
1221 				}
1222 				ps_printf(psdoc, "[ /Separation (%s)\n", spotcolor->name);
1223 				switch(spotcolor->colorspace) {
1224 					case PS_COLORSPACE_GRAY:
1225 						ps_printf(psdoc, "  /DeviceGray { 1 %f sub mul 1 exch sub }\n", spotcolor->c1);
1226 						break;
1227 					case PS_COLORSPACE_RGB: {
1228 						float max;
1229 						max = (spotcolor->c1 > spotcolor->c2) ? spotcolor->c1 : spotcolor->c2;
1230 						max = (max > spotcolor->c3) ? max : spotcolor->c3;
1231 						ps_printf(psdoc, "  /DeviceRGB { 1 exch sub dup dup %f exch sub %f mul add exch dup dup %f exch sub %f mul add exch dup %f exch sub %f mul add }\n", max, spotcolor->c1, max, spotcolor->c2, max, spotcolor->c3);
1232 						break;
1233 					}
1234 					case PS_COLORSPACE_CMYK:
1235 						ps_printf(psdoc, "  /DeviceCMYK { dup %f mul exch dup %f mul exch dup %f mul exch %f mul }\n", spotcolor->c1, spotcolor->c2, spotcolor->c3, spotcolor->c4);
1236 						break;
1237 				}
1238 				ps_printf(psdoc, "] setcolorspace\n");
1239 				ps_printf(psdoc, "%f setcolor\n", currentcolor->c2);
1240 				break;
1241 			}
1242 		}
1243 	}
1244 }
1245 /* }}} */
1246 
1247 /* PS_open_fp() {{{
1248  * Associates an already open file with the PostScript document created
1249  * with PS_new().
1250  */
1251 PSLIB_API int PSLIB_CALL
PS_open_fp(PSDoc * psdoc,FILE * fp)1252 PS_open_fp(PSDoc *psdoc, FILE *fp) {
1253 	if(NULL == fp) {
1254 		ps_error(psdoc, PS_Warning, _("File pointer is NULL. Use PS_open_mem() to create file in memory."));
1255 		return(-1);
1256 	}
1257 	psdoc->fp = fp;
1258 	psdoc->closefp = ps_false;
1259 	psdoc->writeproc = ps_writeproc_file;
1260 	psdoc->page = 0;
1261 	psdoc->doc_open = ps_true;
1262 	ps_enter_scope(psdoc, PS_SCOPE_DOCUMENT);
1263 
1264 	return(0);
1265 }
1266 /* }}} */
1267 
1268 /* PS_open_file() {{{
1269  * Associates a file to the PostScript document created with PS_new().
1270  */
1271 PSLIB_API int PSLIB_CALL
PS_open_file(PSDoc * psdoc,const char * filename)1272 PS_open_file(PSDoc *psdoc, const char *filename) {
1273 	FILE *fp;
1274 
1275 	if(filename == NULL || filename[0] == '\0' || (filename[0] == '-' && filename[1] == '\0')) {
1276 		PS_open_mem(psdoc, NULL);
1277 		return 0;
1278 	} else {
1279 		fp = fopen(filename, "w");
1280 		if(NULL == fp) {
1281 			ps_error(psdoc, PS_IOError, _("Could not open file '%s'."), filename);
1282 			return -1;
1283 		}
1284 
1285 		if(0 > PS_open_fp(psdoc, fp)) {
1286 			fclose(fp);
1287 			return(-1);
1288 		}
1289 		psdoc->closefp = ps_true;
1290 	}
1291 	return 0;
1292 }
1293 /* }}} */
1294 
1295 /* PS_open_mem() {{{
1296  * Create document in memory. Actually you are just passing a write procedure
1297  * which is used instead of the internal procedure.
1298  */
1299 PSLIB_API int PSLIB_CALL
PS_open_mem(PSDoc * p,size_t (* writeproc)(PSDoc * p,void * data,size_t size))1300 PS_open_mem(PSDoc *p, size_t (*writeproc)(PSDoc *p, void *data, size_t size)) {
1301 	if (writeproc == NULL) {
1302 		p->sb = str_buffer_new(p, 1000);
1303 		p->writeproc = ps_writeproc_buffer;
1304 	} else {
1305 		p->writeproc = writeproc;
1306 	}
1307 	p->fp = NULL;
1308 	p->page = 0;
1309 	p->doc_open = ps_true;
1310 	ps_enter_scope(p, PS_SCOPE_DOCUMENT);
1311 
1312 	return 0;
1313 }
1314 /* }}} */
1315 
1316 /* _output_bookmarks() {{{
1317  *
1318  */
_output_bookmarks(PSDoc * psdoc,DLIST * bookmarks)1319 void _output_bookmarks(PSDoc *psdoc, DLIST *bookmarks) {
1320 	PS_BOOKMARK *bm;
1321 	for(bm = dlst_last(bookmarks); bm != NULL; bm = dlst_prev(bm)) {
1322 		char *str;
1323 		ps_printf(psdoc, "[ /Page %d /Title (", bm->page);
1324 		str = bm->text;
1325 		while(*str != '\0') {
1326 			unsigned char index = (unsigned char) *str;
1327 			/* Everything below 32, above 127 and '(' and ')' should be output
1328 			 * as octal values.
1329 			 */
1330 			if(index < 32 || index > 127 || index == '(' || index == ')' || index == '\\') {
1331 				ps_printf(psdoc, "\\%03o", index);
1332 			} else {
1333 				ps_putc(psdoc, *str);
1334 			}
1335 			str++;
1336 		}
1337 		ps_printf(psdoc, ") /Count %d /OUT pdfmark\n", (bm->open == 0) ? bm->children->count : -bm->children->count);
1338 		if(bm->children->count)
1339 			_output_bookmarks(psdoc, bm->children);
1340 	}
1341 }
1342 /* }}} */
1343 
1344 /* PS_close() {{{
1345  * Closes a PostScript document. It closes the actual file only if the
1346  * document was opened with PS_open_file(). This function does not
1347  * free resources. Use PS_delete() to do that.
1348  */
1349 PSLIB_API void PSLIB_CALL
PS_close(PSDoc * psdoc)1350 PS_close(PSDoc *psdoc) {
1351 	/* End page if it is still open */
1352 	if(psdoc->page_open == ps_true) {
1353 		ps_error(psdoc, PS_Warning, _("Ending last page of document."));
1354 		PS_end_page(psdoc);
1355 	}
1356 	if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT)) {
1357 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' scope."), __FUNCTION__);
1358 		return;
1359 	}
1360 
1361 	/* Write the trailer if the document was not close before */
1362 	if(psdoc->doc_open == ps_true) {
1363 		ps_printf(psdoc, "%%%%Trailer\n");
1364 		ps_printf(psdoc, "end\n");
1365 		if(psdoc->bookmarks->count > 0) {
1366 			_output_bookmarks(psdoc, psdoc->bookmarks);
1367 		}
1368 		ps_printf(psdoc, "%%%%Pages: %d\n", psdoc->page);
1369 		ps_printf(psdoc, "%%%%BoundingBox: %s\n", psdoc->BoundingBox);
1370 		ps_printf(psdoc, "%%%%Orientation: %s\n", psdoc->Orientation);
1371 		ps_printf(psdoc, "%%%%EOF");
1372 		ps_leave_scope(psdoc, PS_SCOPE_DOCUMENT);
1373 	}
1374 
1375 	/* FIXME: Need to free the linked lists parameters, categories and values */
1376 	if((psdoc->closefp == ps_true) && (NULL != psdoc->fp)) {
1377 		fclose(psdoc->fp);
1378 		psdoc->fp = NULL;
1379 	}
1380 
1381 	psdoc->doc_open = ps_false;
1382 }
1383 /* }}} */
1384 
1385 /* PS_delete() {{{
1386  * Frees all resources of a document. If the document was not closed before,
1387  * it will also be closed.
1388  */
1389 PSLIB_API void PSLIB_CALL
PS_delete(PSDoc * psdoc)1390 PS_delete(PSDoc *psdoc) {
1391 	int i;
1392 	if(NULL == psdoc) {
1393 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
1394 		return;
1395 	}
1396 
1397 	/* Make sure the document is closed */
1398 	if(psdoc->doc_open == ps_true) {
1399 		PS_close(psdoc);
1400 	}
1401 
1402 	if(psdoc->sb)
1403 		str_buffer_delete(psdoc, psdoc->sb);
1404 
1405 	/* Free the memory */
1406 	ps_del_resources(psdoc);
1407 	ps_del_parameters(psdoc);
1408 	ps_del_values(psdoc);
1409 	ps_del_bookmarks(psdoc, psdoc->bookmarks);
1410 	psdoc->bookmarks = NULL;
1411 
1412 	if(psdoc->Author) {
1413 		psdoc->free(psdoc, psdoc->Author);
1414 		psdoc->Author = NULL;
1415 	}
1416 	if(psdoc->Keywords) {
1417 		psdoc->free(psdoc, psdoc->Keywords);
1418 		psdoc->Keywords = NULL;
1419 	}
1420 	if(psdoc->Subject) {
1421 		psdoc->free(psdoc, psdoc->Subject);
1422 		psdoc->Subject = NULL;
1423 	}
1424 	if(psdoc->Title) {
1425 		psdoc->free(psdoc, psdoc->Title);
1426 		psdoc->Title = NULL;
1427 	}
1428 	if(psdoc->Creator) {
1429 		psdoc->free(psdoc, psdoc->Creator);
1430 		psdoc->Creator = NULL;
1431 	}
1432 	if(psdoc->BoundingBox) {
1433 		psdoc->free(psdoc, psdoc->BoundingBox);
1434 		psdoc->BoundingBox = NULL;
1435 	}
1436 	if(psdoc->Orientation) {
1437 		psdoc->free(psdoc, psdoc->Orientation);
1438 		psdoc->Orientation = NULL;
1439 	}
1440 	if(psdoc->CreationDate) {
1441 		psdoc->free(psdoc, psdoc->CreationDate);
1442 		psdoc->CreationDate = NULL;
1443 	}
1444 
1445 	/* Freeing font resources */
1446 	i = 0;
1447 	while(i < psdoc->fontcnt) {
1448 		if(psdoc->fonts[i]) {
1449 			_ps_delete_font(psdoc, psdoc->fonts[i]);
1450 		}
1451 		i++;
1452 	}
1453 	psdoc->free(psdoc, psdoc->fonts);
1454 
1455 	/* Freeing image resources */
1456 	i = 0;
1457 	while(i < psdoc->imagecnt) {
1458 		if(psdoc->images[i]) {
1459 			_ps_delete_image(psdoc, psdoc->images[i]);
1460 		}
1461 		i++;
1462 	}
1463 	psdoc->free(psdoc, psdoc->images);
1464 
1465 	/* Freeing pattern resources */
1466 	i = 0;
1467 	while(i < psdoc->patterncnt) {
1468 		if(psdoc->patterns[i]) {
1469 			_ps_delete_pattern(psdoc, psdoc->patterns[i]);
1470 		}
1471 		i++;
1472 	}
1473 	psdoc->free(psdoc, psdoc->patterns);
1474 
1475 	/* Freeing spotcolor resources */
1476 	i = 0;
1477 	while(i < psdoc->spotcolorcnt) {
1478 		if(psdoc->spotcolors[i]) {
1479 			_ps_delete_spotcolor(psdoc, psdoc->spotcolors[i]);
1480 		}
1481 		i++;
1482 	}
1483 	psdoc->free(psdoc, psdoc->spotcolors);
1484 
1485 	/* Freeing shading resources */
1486 	i = 0;
1487 	while(i < psdoc->shadingcnt) {
1488 		if(psdoc->shadings[i]) {
1489 			_ps_delete_shading(psdoc, psdoc->shadings[i]);
1490 		}
1491 		i++;
1492 	}
1493 	psdoc->free(psdoc, psdoc->shadings);
1494 
1495 	/* Freeing graphic state resources */
1496 	i = 0;
1497 	while(i < psdoc->gstatecnt) {
1498 		if(psdoc->gstates[i]) {
1499 			_ps_delete_gstate(psdoc, psdoc->gstates[i]);
1500 		}
1501 		i++;
1502 	}
1503 	psdoc->free(psdoc, psdoc->gstates);
1504 
1505 	if(psdoc->hdict)
1506 		hnj_hyphen_free(psdoc->hdict);
1507 	if(psdoc->hdictfilename)
1508 		psdoc->free(psdoc, psdoc->hdictfilename);
1509 	psdoc->free(psdoc, psdoc);
1510 }
1511 /* }}} */
1512 
1513 /* PS_set_parameter() {{{
1514  * Sets all kind of parameters. Parameters are string values as opposed
1515  * to 'values' set by PS_set_value() which are of type float.
1516  * Some parameters change internal variables while other are just
1517  * stored in double linked list. If a value itself is a name value pair,
1518  * then this is called a resource.
1519  */
1520 PSLIB_API void PSLIB_CALL
PS_set_parameter(PSDoc * psdoc,const char * name,const char * value)1521 PS_set_parameter(PSDoc *psdoc, const char *name, const char *value) {
1522 	if(NULL == psdoc) {
1523 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
1524 		return;
1525 	}
1526 	if((strcmp(name, "FontAFM") == 0) ||
1527 		 (strcmp(name, "FontOutline") == 0) ||
1528 		 (strcmp(name, "FontProtusion") == 0) ||
1529 		 (strcmp(name, "FontEncoding") == 0) ||
1530 		 (strcmp(name, "RightMarginKerning") == 0) ||
1531 		 (strcmp(name, "LeftMarginKerning") == 0)) {
1532 		char *res = ps_strdup(psdoc, value);
1533 		char *filename;
1534 
1535 		if ((filename = strchr(res, '=')) == NULL) {
1536 			psdoc->free(psdoc, res);
1537 			ps_error(psdoc, PS_Warning, _("Invalid resource"));
1538 			return;
1539 		}
1540 		*filename++ = '\0';
1541 		if (*filename == '=') {
1542 			filename++;
1543 		}
1544 		if(strcmp(name, "RightMarginKerning") == 0) {
1545 			ADOBEINFO *ai;
1546 			if(!psdoc->font || !psdoc->font->metrics) {
1547 				ps_error(psdoc, PS_RuntimeError, _("RightMarginKerning cannot be set without setting a font before."));
1548 				return;
1549 			}
1550 			ai = gfindadobe(psdoc->font->metrics->gadobechars, res);
1551 			if(ai)
1552 				ai->rkern = atoi(filename);
1553 		} else if(strcmp(name, "LeftMarginKerning") == 0) {
1554 			ADOBEINFO *ai;
1555 			if(!psdoc->font || !psdoc->font->metrics) {
1556 				ps_error(psdoc, PS_RuntimeError, _("LeftMarginKerning cannot be set without setting a font before."));
1557 				return;
1558 			}
1559 			ai = gfindadobe(psdoc->font->metrics->gadobechars, res);
1560 			if(ai)
1561 				ai->lkern = atoi(filename);
1562 		} else {
1563 			if(NULL == ps_add_resource(psdoc, name, res, filename, NULL)) {
1564 				ps_error(psdoc, PS_RuntimeError, _("Resource '%s' in category '%s' could not be registered."), res, name);
1565 			}
1566 		}
1567 		psdoc->free(psdoc, res);
1568 	} else if(strcmp(name, "SearchPath") == 0) {
1569 		if(NULL == ps_add_resource(psdoc, name, NULL, value, NULL)) {
1570 			ps_error(psdoc, PS_RuntimeError, _("Resource in category '%s' could not be registered."), name);
1571 		}
1572 	} else if(strcmp(name, "underline") == 0) {
1573 		if(strcmp(value, "true") == 0) {
1574 			psdoc->underline = ps_true;
1575 		} else {
1576 			psdoc->underline = ps_false;
1577 		}
1578 	} else if(strcmp(name, "overline") == 0) {
1579 		if(strcmp(value, "true") == 0) {
1580 			psdoc->overline = ps_true;
1581 		} else {
1582 			psdoc->overline = ps_false;
1583 		}
1584 	} else if(strcmp(name, "strikeout") == 0) {
1585 		if(strcmp(value, "true") == 0) {
1586 			psdoc->strikeout = ps_true;
1587 		} else {
1588 			psdoc->strikeout = ps_false;
1589 		}
1590 	} else if(strcmp(name, "warning") == 0) {
1591 		if(strcmp(value, "true") == 0) {
1592 			psdoc->warnings = ps_true;
1593 		} else {
1594 			psdoc->warnings = ps_false;
1595 		}
1596 	} else if(strcmp(name, "hyphendict") == 0) {
1597 		if((psdoc->hdict != NULL) && strcmp(value, psdoc->hdictfilename)) {
1598 			hnj_hyphen_free(psdoc->hdict);
1599 			psdoc->free(psdoc, psdoc->hdictfilename);
1600 		}
1601 		psdoc->hdict = hnj_hyphen_load(value);
1602 		if(!psdoc->hdict) {
1603 			ps_error(psdoc, PS_RuntimeError, _("Could not load hyphenation table '%s', turning hyphenation off."), value);
1604 			return;
1605 		}
1606 		if(psdoc->hdictfilename)
1607 			psdoc->free(psdoc, psdoc->hdictfilename);
1608 		psdoc->hdictfilename = ps_strdup(psdoc, value);
1609 	} else if(strcmp(name, "inputencoding") == 0) {
1610 		ENCODING *enc;
1611 		if(NULL != (enc = ps_get_inputencoding(value))) {
1612 			psdoc->inputenc = enc;
1613 		} else {
1614 			ps_error(psdoc, PS_Warning, _("Input encoding '%s' could not be set."), value);
1615 		}
1616 		if(strcmp(value, "true") == 0) {
1617 			psdoc->warnings = ps_true;
1618 		} else {
1619 			psdoc->warnings = ps_false;
1620 		}
1621 	} else {
1622 		PS_PARAMETER *parameter;
1623 
1624 		/* Check if parameter already exists. If yes, reset it */
1625 		for(parameter = dlst_first(psdoc->parameters); parameter != NULL; parameter = dlst_next(parameter)) {
1626 			if (0 == strcmp(parameter->name, name)) {
1627 				psdoc->free(psdoc, parameter->value);
1628 				parameter->value = ps_strdup(psdoc, value);
1629 				return;
1630 			}
1631 		}
1632 
1633 		/* Add an new parameter */
1634 		if(NULL == (parameter = (PS_PARAMETER *) dlst_newnode(psdoc->parameters, (int) sizeof(PS_PARAMETER)))) {
1635 			return;
1636 		}
1637 		parameter->name = ps_strdup(psdoc, name);
1638 		parameter->value = ps_strdup(psdoc, value);
1639 		dlst_insertafter(psdoc->parameters, parameter, PS_DLST_HEAD(psdoc->parameters));
1640 	}
1641 }
1642 /* }}} */
1643 
1644 /* PS_get_parameter() {{{
1645  * Returns a parameter. This function cannot return resources set by
1646  * PS_set_parameter().
1647  */
1648 PSLIB_API const char * PSLIB_CALL
PS_get_parameter(PSDoc * psdoc,const char * name,float modifier)1649 PS_get_parameter(PSDoc *psdoc, const char *name, float modifier) {
1650 	PS_PARAMETER *param;
1651 
1652 	if(NULL == psdoc) {
1653 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
1654 		return(NULL);
1655 	}
1656 
1657 	if(name == NULL) {
1658 		ps_error(psdoc, PS_RuntimeError, _("Do not know which parameter to get since the passed name is NULL."));
1659 		return(NULL);
1660 	}
1661 
1662 	if(strcmp(name, "fontname") == 0) {
1663 		PSFont *psfont;
1664 		if(0 == (int) modifier) {
1665 			psfont = psdoc->font;
1666 		} else {
1667 			if(NULL == (psfont = _ps_get_font(psdoc, (int) modifier)))
1668 				return(NULL);
1669 		}
1670 		if(NULL == psfont || NULL == psfont->metrics) {
1671 			ps_error(psdoc, PS_RuntimeError, _("No font set."));
1672 			return(NULL);
1673 		}
1674 		return(psfont->metrics->fontname);
1675 	} else if(strcmp(name, "fontencoding") == 0) {
1676 		PSFont *psfont;
1677 		if(0 == (int) modifier) {
1678 			psfont = psdoc->font;
1679 		} else {
1680 			if(NULL == (psfont = _ps_get_font(psdoc, (int) modifier)))
1681 				return(NULL);
1682 		}
1683 		if(NULL == psfont || NULL == psfont->metrics) {
1684 			ps_error(psdoc, PS_RuntimeError, _("No font set."));
1685 			return(NULL);
1686 		}
1687 		return(psfont->metrics->codingscheme);
1688 	} else if(strcmp(name, "dottedversion") == 0) {
1689 		return(LIBPS_DOTTED_VERSION);
1690 	} else if(strcmp(name, "scope") == 0) {
1691 		switch(ps_current_scope(psdoc)) {
1692 			case PS_SCOPE_OBJECT:
1693 				return("object");
1694 			case PS_SCOPE_DOCUMENT:
1695 				return("document");
1696 			case PS_SCOPE_NONE:
1697 				return("null");
1698 			case PS_SCOPE_PAGE:
1699 				return("page");
1700 			case PS_SCOPE_PATTERN:
1701 				return("pattern");
1702 			case PS_SCOPE_PATH:
1703 				return("path");
1704 			case PS_SCOPE_TEMPLATE:
1705 				return("template");
1706 			case PS_SCOPE_PROLOG:
1707 				return("prolog");
1708 			case PS_SCOPE_FONT:
1709 				return("font");
1710 			case PS_SCOPE_GLYPH:
1711 				return("glyph");
1712 		}
1713 	} else {
1714 		for(param = dlst_first(psdoc->parameters); param != NULL; param = dlst_next(param)) {
1715 			if (0 == strcmp(param->name, name)) {
1716 				return(param->value);
1717 			}
1718 		}
1719 	}
1720 	return(NULL);
1721 }
1722 /* }}} */
1723 
1724 /* PS_set_value() {{{
1725  * Sets a float value.
1726  */
1727 PSLIB_API void PSLIB_CALL
PS_set_value(PSDoc * psdoc,const char * name,float value)1728 PS_set_value(PSDoc *psdoc, const char *name, float value) {
1729 	PS_VALUE *parameter;
1730 
1731 	if(NULL == psdoc) {
1732 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
1733 		return;
1734 	}
1735 
1736 	if(strcmp(name, "wordspacing") == 0) {
1737 		ADOBEINFO *ai = NULL;
1738 		if(psdoc->font && psdoc->font->metrics != NULL)
1739 			ai = gfindadobe(psdoc->font->metrics->gadobechars, "space");
1740 		if(ai)
1741 			psdoc->font->wordspace = (int) (value * ai->width);
1742 	} else if(strcmp(name, "textx") == 0) {
1743 		psdoc->tstates[psdoc->tstate].tx = value;
1744 		psdoc->tstates[psdoc->tstate].cx = value;
1745 	} else if(strcmp(name, "texty") == 0) {
1746 		psdoc->tstates[psdoc->tstate].ty = value;
1747 		psdoc->tstates[psdoc->tstate].cy = value;
1748 	} else if(strcmp(name, "textrendering") == 0) {
1749 		psdoc->textrendering = (int) value;
1750 	} else {
1751 		/* Check if value exists */
1752 		for(parameter = dlst_first(psdoc->values); parameter != NULL; parameter = dlst_next(parameter)) {
1753 			if(0 == strcmp(parameter->name, name)) {
1754 				parameter->value = value;
1755 				return;
1756 			}
1757 		}
1758 
1759 		/* Doesn't exist, so create a new one */
1760 		if(NULL == (parameter = (PS_VALUE *) dlst_newnode(psdoc->values, (int) sizeof(PS_VALUE)))) {
1761 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for new node in value list."));
1762 			return;
1763 		}
1764 		parameter->name = ps_strdup(psdoc, name);
1765 		parameter->value = value;
1766 		dlst_insertafter(psdoc->values, parameter, PS_DLST_HEAD(psdoc->values));
1767 	}
1768 
1769 }
1770 /* }}} */
1771 
1772 /* PS_get_value() {{{
1773  * Returns a value set with PS_set_value()
1774  * modifier specifies a for which object this value shall be retrieved.
1775  * objects are fonts, images, ...
1776  */
1777 PSLIB_API float PSLIB_CALL
PS_get_value(PSDoc * psdoc,const char * name,float modifier)1778 PS_get_value(PSDoc *psdoc, const char *name, float modifier) {
1779 	PS_VALUE *value;
1780 
1781 	if(name == NULL) {
1782 		ps_error(psdoc, PS_RuntimeError, _("Do not know which value to get since the passed name is NULL."));
1783 		return(0.0);
1784 	}
1785 	if(NULL == psdoc) {
1786 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
1787 		return(0.0);
1788 	}
1789 
1790 	if(strcmp(name, "fontsize") == 0) {
1791 		PSFont *psfont;
1792 		if(0 == (int) modifier) {
1793 			psfont = psdoc->font;
1794 		} else {
1795 			if(NULL == (psfont = _ps_get_font(psdoc, (int) modifier)))
1796 				return(0.0);
1797 		}
1798 		if(NULL == psfont) {
1799 			ps_error(psdoc, PS_RuntimeError, _("No font set."));
1800 			return(0.0);
1801 		}
1802 		return(psfont->size);
1803 	} else if(strcmp(name, "font") == 0) {
1804 		return((float) _ps_find_font(psdoc, psdoc->font));
1805 	} else if(strcmp(name, "imagewidth") == 0) {
1806 		PSImage *psimage = _ps_get_image(psdoc, (int) modifier);
1807 		if(!psimage)
1808 			return(0.0);
1809 		return((float) psimage->width);
1810 	} else if(strcmp(name, "imageheight") == 0) {
1811 		PSImage *psimage = _ps_get_image(psdoc, (int) modifier);
1812 		if(!psimage)
1813 			return(0.0);
1814 		return((float) psimage->height);
1815 	} else if(strcmp(name, "capheight") == 0) {
1816 		PSFont *psfont;
1817 		if(0 == (int) modifier) {
1818 			psfont = psdoc->font;
1819 		} else {
1820 			if(NULL == (psfont = _ps_get_font(psdoc, (int) modifier)))
1821 				return(0.0);
1822 		}
1823 		if(NULL == psfont || NULL == psfont->metrics) {
1824 			ps_error(psdoc, PS_RuntimeError, _("No font set."));
1825 			return(0.0);
1826 		}
1827 		return(psfont->metrics->capheight);
1828 	} else if(strcmp(name, "ascender") == 0) {
1829 		PSFont *psfont;
1830 		if(0 == (int) modifier) {
1831 			psfont = psdoc->font;
1832 		} else {
1833 			if(NULL == (psfont = _ps_get_font(psdoc, (int) modifier)))
1834 				return(0.0);
1835 		}
1836 		if(NULL == psfont || NULL == psfont->metrics) {
1837 			ps_error(psdoc, PS_RuntimeError, _("No font set."));
1838 			return(0.0);
1839 		}
1840 		return(psfont->metrics->ascender);
1841 	} else if(strcmp(name, "descender") == 0) {
1842 		PSFont *psfont;
1843 		if(0 == (int) modifier) {
1844 			psfont = psdoc->font;
1845 		} else {
1846 			if(NULL == (psfont = _ps_get_font(psdoc, (int) modifier)))
1847 				return(0.0);
1848 		}
1849 		if(NULL == psfont || NULL == psfont->metrics) {
1850 			ps_error(psdoc, PS_RuntimeError, _("No font set."));
1851 			return(0.0);
1852 		}
1853 		return(psfont->metrics->descender);
1854 	} else if(strcmp(name, "italicangle") == 0) {
1855 		PSFont *psfont;
1856 		if(0 == (int) modifier) {
1857 			psfont = psdoc->font;
1858 		} else {
1859 			if(NULL == (psfont = _ps_get_font(psdoc, (int) modifier)))
1860 				return(0.0);
1861 		}
1862 		if(NULL == psfont || NULL == psfont->metrics) {
1863 			ps_error(psdoc, PS_RuntimeError, _("No font set."));
1864 			return(0.0);
1865 		}
1866 		return(psfont->metrics->italicangle);
1867 	} else if(strcmp(name, "underlineposition") == 0) {
1868 		PSFont *psfont;
1869 		if(0 == (int) modifier) {
1870 			psfont = psdoc->font;
1871 		} else {
1872 			if(NULL == (psfont = _ps_get_font(psdoc, (int) modifier)))
1873 				return(0.0);
1874 		}
1875 		if(NULL == psfont || NULL == psfont->metrics) {
1876 			ps_error(psdoc, PS_RuntimeError, _("No font set."));
1877 			return(0.0);
1878 		}
1879 		return(psfont->metrics->underlineposition);
1880 	} else if(strcmp(name, "underlinethickness") == 0) {
1881 		PSFont *psfont;
1882 		if(0 == (int) modifier) {
1883 			psfont = psdoc->font;
1884 		} else {
1885 			if(NULL == (psfont = _ps_get_font(psdoc, (int) modifier)))
1886 				return(0.0);
1887 		}
1888 		if(NULL == psfont || NULL == psfont->metrics) {
1889 			ps_error(psdoc, PS_RuntimeError, _("No font set."));
1890 			return(0.0);
1891 		}
1892 		return(psfont->metrics->underlinethickness);
1893 	} else if(strcmp(name, "textx") == 0) {
1894 		return(psdoc->tstates[psdoc->tstate].tx);
1895 	} else if(strcmp(name, "texty") == 0) {
1896 		return(psdoc->tstates[psdoc->tstate].ty);
1897 	} else if(strcmp(name, "textrendering") == 0) {
1898 		return((float) psdoc->textrendering);
1899 	} else if(strcmp(name, "wordspacing") == 0) {
1900 		ADOBEINFO *ai = NULL;
1901 		if(psdoc->font != NULL && psdoc->font->metrics != NULL)
1902 			ai = gfindadobe(psdoc->font->metrics->gadobechars, "space");
1903 		if(ai)
1904 			return((float) psdoc->font->wordspace / ai->width);
1905 		else
1906 			return(0.0);
1907 	} else if(strcmp(name, "major") == 0) {
1908 		return((float) LIBPS_MAJOR_VERSION);
1909 	} else if(strcmp(name, "minor") == 0) {
1910 		return((float) LIBPS_MINOR_VERSION);
1911 	} else if(strcmp(name, "subminor") == 0) {
1912 		return((float) LIBPS_MICRO_VERSION);
1913 	} else if(strcmp(name, "revision") == 0) {
1914 		return((float) LIBPS_MICRO_VERSION);
1915 	} else {
1916 		for(value = dlst_first(psdoc->values); value != NULL; value = dlst_next(value)) {
1917 			if (0 == strcmp(value->name, name)) {
1918 				return(value->value);
1919 			}
1920 		}
1921 	}
1922 	return(0.0);
1923 }
1924 /* }}} */
1925 
1926 /* PS_list_values() {{{
1927  * Outputs a list of all values
1928  */
1929 PSLIB_API void PSLIB_CALL
PS_list_values(PSDoc * psdoc)1930 PS_list_values(PSDoc *psdoc) {
1931 	PS_VALUE *value;
1932 
1933 	if(psdoc == NULL) {
1934 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
1935 		return;
1936 	}
1937 
1938 	printf("List of Values\n-----------------------------------\n");
1939 	for(value = dlst_first(psdoc->values); value != NULL; value = dlst_next(value)) {
1940 		printf("%s = %f\n", value->name, value->value);
1941 	}
1942 	printf("-------------------------------------\n");
1943 }
1944 /* }}} */
1945 
1946 /* PS_list_parameters() {{{
1947  * Outputs a list of all parameters
1948  */
1949 PSLIB_API void PSLIB_CALL
PS_list_parameters(PSDoc * psdoc)1950 PS_list_parameters(PSDoc *psdoc) {
1951 	PS_PARAMETER *parameter;
1952 
1953 	if(psdoc == NULL) {
1954 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
1955 		return;
1956 	}
1957 
1958 	printf("List of Parameters\n-----------------------------------\n");
1959 	for(parameter = dlst_first(psdoc->parameters); parameter != NULL; parameter = dlst_next(parameter)) {
1960 		printf("%s = %s\n", parameter->name, parameter->value);
1961 	}
1962 	printf("-------------------------------------\n");
1963 }
1964 /* }}} */
1965 
1966 /* PS_list_resources() {{{
1967  * Outputs a list of all resources
1968  */
1969 PSLIB_API void PSLIB_CALL
PS_list_resources(PSDoc * psdoc)1970 PS_list_resources(PSDoc *psdoc) {
1971 	PS_CATEGORY *cat;
1972 	PS_RESOURCE *res;
1973 
1974 	if(psdoc == NULL) {
1975 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
1976 		return;
1977 	}
1978 
1979 	printf("List of Resources\n-----------------------------------\n");
1980 	for(cat = dlst_first(psdoc->categories); cat != NULL; cat = dlst_next(cat)) {
1981 		for(res = dlst_first(cat->resources); res != NULL; res = dlst_next(res)) {
1982 			printf("%s : %s = %s\n", cat->name, res->name, res->value);
1983 		}
1984 	}
1985 	printf("-------------------------------------\n");
1986 }
1987 /* }}} */
1988 
1989 /* PS_begin_page() {{{
1990  * starts a new (the next) page
1991  */
1992 PSLIB_API void PSLIB_CALL
PS_begin_page(PSDoc * psdoc,float width,float height)1993 PS_begin_page(PSDoc *psdoc, float width, float height) {
1994 	int sepcolor;
1995 	if(NULL == psdoc) {
1996 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
1997 		return;
1998 	}
1999 //	if(psdoc->commentswritten == ps_false) {
2000 	if(psdoc->page == 0) {
2001 		/* Do not overwrite the BoundingBox if it has been set before */
2002 		if(width != 0.0 && height != 0.0) {
2003 			if(psdoc->BoundingBox == NULL) {
2004 				char tmp[30];
2005 #ifdef HAVE_SNPRINTF
2006 				snprintf(tmp, 30, "0 0 %.0f %.0f", width, height);
2007 #else
2008 				sprintf(tmp, "0 0 %.0f %.0f", width, height);
2009 #endif
2010 				psdoc->BoundingBox = ps_strdup(psdoc, tmp);
2011 			}
2012 			if(psdoc->Orientation == NULL) {
2013 				if(width > height)
2014 					psdoc->Orientation = ps_strdup(psdoc, "Landscape");
2015 				else
2016 					psdoc->Orientation = ps_strdup(psdoc, "Portrait");
2017 			}
2018 		}
2019 	}
2020 	/* Make sure the rest of the header is written */
2021 	ps_write_ps_header(psdoc);
2022 	if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT)) {
2023 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' scope."), __FUNCTION__);
2024 		return;
2025 	}
2026 
2027 	(psdoc->page)++;
2028 	ps_printf(psdoc, "\n%%%%Page: %i %i\n", psdoc->page, psdoc->page);
2029 	ps_printf(psdoc, "%%%%PageBoundingBox: 0 0 %d %d\n", (int) width, (int) height);
2030 	ps_printf(psdoc, "%%%%BeginPageSetup\n");
2031 	ps_printf(psdoc, "[ /CropBox [0 0 %.2f %.2f] /PAGE pdfmark\n", width, height);
2032 	sepcolor = (int) PS_get_value(psdoc, "separationcolor", 0.0);
2033 	if(sepcolor > 0) {
2034 		ps_printf(psdoc, "userdict 0 %d bop-hook\n", sepcolor-1);
2035 		ps_printf(psdoc, "PslibDict begin ");
2036 		ps_printf(psdoc, "/vsize %.2f def ", height);
2037 		ps_printf(psdoc, "/hsize %.2f def ", width);
2038 		ps_printf(psdoc, "end\n");
2039 	}
2040 	ps_printf(psdoc, "%%%%EndPageSetup\n");
2041 	ps_printf(psdoc, "save\n");
2042 	ps_printf(psdoc, "0 0 %.2f %.2f ", width, height);
2043 	ps_printf(psdoc, "%i PslibPageBeginHook\n", psdoc->page);
2044 	ps_printf(psdoc, "restore\n");
2045 	ps_printf(psdoc, "save\n");
2046 	psdoc->tstates[psdoc->tstate].tx = 100.0;
2047 	psdoc->tstates[psdoc->tstate].ty = 100.0;
2048 	psdoc->tstates[psdoc->tstate].cx = 100.0;
2049 	psdoc->tstates[psdoc->tstate].cy = 100.0;
2050 	psdoc->page_open = ps_true;
2051 	ps_enter_scope(psdoc, PS_SCOPE_PAGE);
2052 }
2053 /* }}} */
2054 
2055 /* PS_end_page() {{{
2056  * ends the page and increments the pagecount by 1
2057  */
2058 PSLIB_API void PSLIB_CALL
PS_end_page(PSDoc * psdoc)2059 PS_end_page(PSDoc *psdoc) {
2060 	if(NULL == psdoc) {
2061 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
2062 		return;
2063 	}
2064 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE)) {
2065 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page' scope."), __FUNCTION__);
2066 		return;
2067 	}
2068 	if(psdoc->agstate > 0) {
2069 		ps_error(psdoc, PS_RuntimeError, _("PS_save() has been called more often than PS_restore()."));
2070 		return;
2071 	}
2072 //	ps_printf(psdoc, "end\n");
2073 	ps_printf(psdoc, "restore\n");
2074 	ps_printf(psdoc, "save\n");
2075 	ps_printf(psdoc, "%i PslibPageEndHook\n", psdoc->page);
2076 	ps_printf(psdoc, "restore\n");
2077 	ps_printf(psdoc, "showpage\n");
2078 	psdoc->page_open = ps_false;
2079 	/* Set current font to NULL in order to enforce calling PS_set_font().
2080 	 * PS_set_font() is needed because each page in encapsulated in save/restore
2081 	 * any setting of a font within page will not be valid after the page.
2082 	 */
2083 	psdoc->font = NULL;
2084 	ps_leave_scope(psdoc, PS_SCOPE_PAGE);
2085 }
2086 /* }}} */
2087 
2088 /* ps_render_text() {{{
2089  * Prints text on page depending on current text rendering.
2090  * The text passed to this function may not containing any
2091  * kerning pairs, since it relies on the PostScript stringwith
2092  * function which cannot handle kerning (unless you don't want
2093  * kerning to be taken into account).
2094  * The text must be font encoded as it is unmodified written into
2095  * the PostScript file.
2096  */
ps_render_text(PSDoc * psdoc,const char * text)2097 static void ps_render_text(PSDoc *psdoc, const char *text) {
2098 	char *str = (char *) text;
2099 	float textrise;
2100 	if(text == NULL)
2101 		return;
2102 	textrise = PS_get_value(psdoc, "textrise", 0.0);
2103 	if(textrise != 0.0) {
2104 		ps_printf(psdoc, "%f tr ", textrise);
2105 	}
2106 	switch(psdoc->textrendering) {
2107 		case -1: /* normal */
2108 		case 1: /* stroke */
2109 		case 5: /* stroke and clip*/
2110 			ps_setcolor(psdoc, PS_COLORTYPE_STROKE);
2111 			break;
2112 		case 0: /* fill */
2113 		case 2: /* stroke and fill */
2114 			// FIXME: need to set fill and stroke color
2115 		case 4: /* fill and clip*/
2116 		case 6: /* fill, stroke and clip*/
2117 			ps_setcolor(psdoc, PS_COLORTYPE_FILL);
2118 			break;
2119 		default:
2120 			ps_setcolor(psdoc, PS_COLORTYPE_STROKE);
2121 	}
2122 	ps_putc(psdoc, '(');
2123 	while(*str != '\0') {
2124 		unsigned char index = (unsigned char) *str;
2125 		/* Everything below 32, above 127 and '(' and ')' should be output
2126 		 * as octal values.
2127 		 */
2128 		if(index < 32 || index > 127 || index == '(' || index == ')' || index == '\\') {
2129 			ps_printf(psdoc, "\\%03o", index);
2130 		} else {
2131 			ps_putc(psdoc, *str);
2132 		}
2133 		str++;
2134 	}
2135 	ps_putc(psdoc, ')');
2136 	switch(psdoc->textrendering) {
2137 		case -1: /* normal */
2138 			ps_puts(psdoc, "p ");
2139 			break;
2140 		case 0: /* fill */
2141 			ps_puts(psdoc, "qf ");
2142 			break;
2143 		case 1: /* stroke */
2144 			ps_puts(psdoc, "qs ");
2145 			break;
2146 		case 2: /* stroke and fill */
2147 			ps_puts(psdoc, "qsf ");
2148 			break;
2149 		case 3: /* invisible text */
2150 			ps_puts(psdoc, "qi ");
2151 			break;
2152 		case 4: /* fill and clip*/
2153 			ps_puts(psdoc, "qfc ");
2154 			break;
2155 		case 5: /* stroke and clip*/
2156 			ps_puts(psdoc, "qsc ");
2157 			break;
2158 		case 6: /* fill, stroke and clip*/
2159 			// FIXME: need to set fill and stroke color
2160 			ps_puts(psdoc, "qfsc ");
2161 			break;
2162 		case 7: /* clip*/
2163 			ps_puts(psdoc, "qc ");
2164 			break;
2165 		default:
2166 			ps_puts(psdoc, "p ");
2167 			break;
2168 	}
2169 	if(textrise) {
2170 		ps_puts(psdoc, "rt ");
2171 	}
2172 }
2173 /* }}} */
2174 
2175 /* PS_show2() {{{
2176  * Output text at current position. Do not print more the xlen characters.
2177  */
2178 PSLIB_API void PSLIB_CALL
PS_show2(PSDoc * psdoc,const char * text,int xlen)2179 PS_show2(PSDoc *psdoc, const char *text, int xlen) {
2180 	int kernonoff;
2181 	int ligonoff;
2182 	char ligdischar;
2183 	float charspacing;
2184 
2185 	if(NULL == psdoc) {
2186 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
2187 		return;
2188 	}
2189 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
2190 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
2191 		return;
2192 	}
2193 	if(NULL == text) {
2194 		ps_error(psdoc, PS_RuntimeError, _("Text to display is NULL."));
2195 		return;
2196 	}
2197 
2198 	if(NULL == psdoc->font) {
2199 		ps_error(psdoc, PS_RuntimeError, _("No font set."));
2200 		return;
2201 	}
2202 
2203 	/* Set starting point for text */
2204 	ps_printf(psdoc, "%.2f %.2f a\n", psdoc->tstates[psdoc->tstate].tx, psdoc->tstates[psdoc->tstate].ty);
2205 
2206 	charspacing = PS_get_value(psdoc, "charspacing", 0) * 1000.0 / psdoc->font->size;
2207 	kernonoff = ps_get_bool_parameter(psdoc, "kerning", 1);
2208 	ligonoff = ps_get_bool_parameter(psdoc, "ligatures", 1);
2209 	if(ligonoff) {
2210 		const char *tmp = PS_get_parameter(psdoc, "ligaturedisolvechar", 0.0);
2211 		if(tmp && tmp[0])
2212 			ligdischar = tmp[0];
2213 		else
2214 			ligdischar = '�';
2215 	}
2216 
2217 	/* Take kerning into account if adobe font metrics has been loaded. */
2218 	if(psdoc->font->metrics) {
2219 		int i, len, k=0;
2220 		float x, y, yy;
2221 		ADOBEINFO *prevai = NULL;
2222 		char *strbuf;
2223 		char *textcpy;
2224 		float overallwidth = 0;
2225 		float ascender = 0;
2226 		float descender = 0;
2227 
2228 		textcpy = ps_strdup(psdoc, text);
2229 		len = strlen(text);
2230 		if(xlen != 0)
2231 			len = xlen < len ? xlen : len;
2232 		strbuf = (char *) psdoc->malloc(psdoc, len+1, _("Allocate temporay space for output string."));
2233 		for(i=0; i<len; i++) {
2234 			unsigned char c;
2235 			char *adobename;
2236 			float kern;
2237 
2238 			c = (unsigned char) textcpy[i];
2239 			adobename = ps_inputenc_name(psdoc, c);
2240 			if(adobename && adobename[0] != '\0') {
2241 				ADOBEINFO *ai;
2242 				ai = gfindadobe(psdoc->font->metrics->gadobechars, adobename);
2243 				if(ai) {
2244 					/* Check if space has ended a string */
2245 					if(strcmp(adobename, "space") == 0) {
2246 						if((kernonoff == 1) && (NULL != prevai))
2247 							kern = (float) calculatekern(prevai, ai);
2248 						else
2249 							kern = 0.0;
2250 						overallwidth += (float) psdoc->font->wordspace + charspacing + kern;
2251 						/* first output collected text */
2252 						if(k > 0) {
2253 							strbuf[k] = '\0';
2254 							ps_render_text(psdoc, strbuf);
2255 							k = 0;
2256 						}
2257 						ps_printf(psdoc, "%.2f w ", (psdoc->font->wordspace+charspacing+kern)*psdoc->font->size/1000.0+0.005);
2258 					} else {
2259 						char *newadobename;
2260 						int offset = 0;
2261 						/* Check if the current and the next character form a ligature */
2262 						if(ligonoff == 1 &&
2263 						   charspacing == 0.0 &&
2264 						   ps_check_for_lig(psdoc, psdoc->font->metrics, ai, &textcpy[i+1], ligdischar, &newadobename, &offset)) {
2265 							if(ps_fontenc_has_glyph(psdoc, psdoc->font->metrics->fontenc, newadobename)) {
2266 								ADOBEINFO *nai = gfindadobe(psdoc->font->metrics->gadobechars, newadobename);
2267 								if(nai) {
2268 									ai = nai;
2269 									i += offset;
2270 								} else {
2271 									ps_error(psdoc, PS_Warning, _("Font '%s' has no ligature '%s', disolving it."), psdoc->font->metrics->fontname, newadobename);
2272 								}
2273 							} else {
2274 								ps_error(psdoc, PS_Warning, _("Font encoding vector of font '%s' has no ligature '%s', disolving it."), psdoc->font->metrics->fontname, newadobename);
2275 							}
2276 						}
2277 						/* At this point either ai is ligature or the current char */
2278 						overallwidth += (float) (ai->width);
2279 						descender = min((float) ai->lly, descender);
2280 						ascender = max((float) ai->ury, ascender);
2281 						if((kernonoff == 1) && (NULL != prevai)) {
2282 							kern = (float) calculatekern(prevai, ai);
2283 							overallwidth += kern;
2284 						} else {
2285 							kern = 0.0;
2286 						}
2287 						if(i < (len-1))
2288 							overallwidth += charspacing;
2289 	//					printf("kern = %f\n", kern);
2290 						/* Any space between last and current character? If Yes output the
2291 						 * collected text first, put the space after it and start a new
2292 						 * string up to the next space (space is either kerning or extra
2293 						 * charspace).
2294 						 */
2295 						if((kern != 0.0 || charspacing != 0.0) && (i > 0)) {
2296 							if(k > 0) {
2297 								strbuf[k] = '\0';
2298 								ps_render_text(psdoc, strbuf);
2299 								k = 0;
2300 							}
2301 							ps_printf(psdoc, "%.2f w ", (kern+charspacing)*psdoc->font->size/1000.0+0.005);
2302 						}
2303 						if(psdoc->font->metrics->fontenc)
2304 							strbuf[k++] = ps_fontenc_code(psdoc, psdoc->font->metrics->fontenc, ai->adobename);
2305 						else
2306 							strbuf[k++] = ai->adobenum;
2307 					}
2308 				} else { /* glyph not found */
2309 					ps_error(psdoc, PS_Warning, _("Glyph '%s' not found in metric file."), adobename);
2310 				}
2311 				prevai = ai;
2312 			} else {
2313 				ps_error(psdoc, PS_Warning, _("Character %d not in input encoding vector."), c);
2314 			}
2315 		}
2316 		psdoc->free(psdoc, textcpy);
2317 		/* Output rest of line if there is some left */
2318 		if(k > 0) {
2319 			strbuf[k] = '\0';
2320 			ps_render_text(psdoc, strbuf);
2321 			k = 0;
2322 		}
2323 		psdoc->free(psdoc, strbuf);
2324 		ps_printf(psdoc, "\n");
2325 		x = psdoc->tstates[psdoc->tstate].tx;
2326 		yy = psdoc->tstates[psdoc->tstate].ty;
2327 		psdoc->tstates[psdoc->tstate].tx += overallwidth*psdoc->font->size/1000.0;
2328 		if(psdoc->underline == ps_true) {
2329 //			y = yy + (psdoc->font->metrics->descender-2*psdoc->font->metrics->underlinethickness)*psdoc->font->size/1000.0;
2330 			y = yy + (descender-2*psdoc->font->metrics->underlinethickness)*psdoc->font->size/1000.0;
2331 			PS_save(psdoc);
2332 			PS_setdash(psdoc, 0, 0);
2333 			PS_setlinewidth(psdoc, psdoc->font->metrics->underlinethickness*psdoc->font->size/1000.0);
2334 			PS_moveto(psdoc, x, y);
2335 			PS_lineto(psdoc, x + overallwidth*psdoc->font->size/1000.0, y);
2336 			PS_stroke(psdoc);
2337 			PS_restore(psdoc);
2338 		}
2339 		if(psdoc->overline == ps_true) {
2340 			y = yy + (psdoc->font->metrics->ascender+2*psdoc->font->metrics->underlinethickness)*psdoc->font->size/1000.0;
2341 			PS_save(psdoc);
2342 			PS_setdash(psdoc, 0, 0);
2343 			PS_setlinewidth(psdoc, psdoc->font->metrics->underlinethickness*psdoc->font->size/1000.0);
2344 			PS_moveto(psdoc, x, y);
2345 			PS_lineto(psdoc, x + overallwidth*psdoc->font->size/1000.0, y);
2346 			PS_stroke(psdoc);
2347 			PS_restore(psdoc);
2348 		}
2349 		if(psdoc->strikeout == ps_true) {
2350 			y = yy + (psdoc->font->metrics->ascender)/2*psdoc->font->size/1000.0;
2351 			PS_save(psdoc);
2352 			PS_setdash(psdoc, 0, 0);
2353 			PS_setlinewidth(psdoc, psdoc->font->metrics->underlinethickness*psdoc->font->size/1000.0);
2354 			PS_moveto(psdoc, x, y);
2355 			PS_lineto(psdoc, x + overallwidth*psdoc->font->size/1000.0, y);
2356 			PS_stroke(psdoc);
2357 			PS_restore(psdoc);
2358 		}
2359 	} else {
2360 		/* FIXME: ps_render_text() expects the text in fontenc */
2361 		ps_render_text(psdoc, text);
2362 	}
2363 }
2364 /* }}} */
2365 
2366 /* PS_show() {{{
2367  * Output null terminated string
2368  */
2369 PSLIB_API void PSLIB_CALL
PS_show(PSDoc * psdoc,const char * text)2370 PS_show(PSDoc *psdoc, const char *text) {
2371 	PS_show2(psdoc, text, 0);
2372 }
2373 /* }}} */
2374 
2375 /* PS_continue_text() {{{
2376  * Output text one line after the last line outputed. The line spacing
2377  * is taken from the value 'leading'.
2378  */
2379 PSLIB_API void PSLIB_CALL
PS_continue_text(PSDoc * psdoc,const char * text)2380 PS_continue_text(PSDoc *psdoc, const char *text) {
2381 	PS_continue_text2(psdoc, text, 0);
2382 }
2383 /* }}} */
2384 
2385 /* PS_continue_text2() {{{
2386  * Output text one line after the last line outputed. The line spacing
2387  * is taken from the value 'leading'. Do not output more than len characters.
2388  */
2389 PSLIB_API void PSLIB_CALL
PS_continue_text2(PSDoc * psdoc,const char * text,int len)2390 PS_continue_text2(PSDoc *psdoc, const char *text, int len) {
2391 	int y, x;
2392 	if(NULL == psdoc) {
2393 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
2394 		return;
2395 	}
2396 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
2397 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
2398 		return;
2399 	}
2400 	/* Save the current text position and set it to the text position
2401 	 * for continued text.
2402 	 */
2403 	y = psdoc->tstates[psdoc->tstate].ty;
2404 	x = psdoc->tstates[psdoc->tstate].tx;
2405 	psdoc->tstates[psdoc->tstate].cy -= PS_get_value(psdoc, "leading", 0.0);
2406 	psdoc->tstates[psdoc->tstate].ty = psdoc->tstates[psdoc->tstate].cy;
2407 	psdoc->tstates[psdoc->tstate].tx = psdoc->tstates[psdoc->tstate].cx;
2408 	PS_show2(psdoc, text, len);
2409 	/* Restore the old text position */
2410 	psdoc->tstates[psdoc->tstate].ty = y;
2411 	psdoc->tstates[psdoc->tstate].tx = x;
2412 }
2413 /* }}} */
2414 
2415 //#define MAX_CHARS_IN_LINE 1024
2416 
2417 /* PS_show_boxed() {{{
2418  * Outputs text in a box with given dimensions. The text is justified
2419  * as specified in hmode. This function uses several parameters and values
2420  * to format the output. The return value is the number of characters that
2421  * could not be written.
2422  */
2423 PSLIB_API int PSLIB_CALL
PS_show_boxed(PSDoc * psdoc,const char * text,float left,float bottom,float width,float height,const char * hmode,const char * feature)2424 PS_show_boxed(PSDoc *psdoc, const char *text, float left, float bottom, float width, float height, const char *hmode, const char *feature) {
2425 	char *str = NULL;
2426 	char *textcopy = NULL;
2427 	char *linebuf = NULL;
2428 	char prevchar = '\0';
2429 	int curpos, lastbreak, lastdelim, firstword, spaces, morechars, mode;
2430 	int lineend, parend, boxlinecounter, parlinecounter, *linecounter;
2431 	int parcounter, parindentskip;
2432 	int numindentlines = 0;
2433 	int doindent;
2434 	const char *linenumbermode;
2435 	float hlen, vlen, xpos, ypos, oldypos, leading, old_word_spacing;
2436 	float oldhlen = 0.0;
2437 	float parindent, parskip, linewidth, linenumberspace;
2438 	float linenumbersep = 5.0;
2439 	int hyphenationonoff;
2440 	int treatcrasspace = 0;
2441 	int treatcraspar = 1;
2442 	int hyphenminchars = 3;
2443 	int blind = 0;
2444 	int fontid;
2445 
2446 	if(NULL == psdoc) {
2447 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
2448 		return 0;
2449 	}
2450 
2451 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
2452 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
2453 		return 0;
2454 	}
2455 
2456 	if(NULL == text)
2457 		return 0;
2458 
2459 	if(NULL == hmode || '\0' == *hmode) {
2460 		ps_error(psdoc, PS_RuntimeError, _("Must specify a horizontal mode for PS_show_boxed()."));
2461 		return 0;
2462 	}
2463 
2464 	if(0 == strcmp(hmode, "fulljustify")) {
2465 		mode = PS_TEXT_HMODE_FULLJUSTIFY;
2466 	} else if(0 == strcmp(hmode, "justify")) {
2467 		mode = PS_TEXT_HMODE_JUSTIFY;
2468 	} else if(0 == strcmp(hmode, "right")) {
2469 		mode = PS_TEXT_HMODE_RIGHT;
2470 	} else if(0 == strcmp(hmode, "center")) {
2471 		mode = PS_TEXT_HMODE_CENTER;
2472 	} else if(0 == strcmp(hmode, "left")) {
2473 		mode = PS_TEXT_HMODE_LEFT;
2474 	} else {
2475 		ps_error(psdoc, PS_Warning, _("There is no such horizontal mode like '%s'. Using 'left' instead."), hmode);
2476 		mode = PS_TEXT_HMODE_LEFT;
2477 	}
2478 
2479 	if(feature != NULL && 0 == strcmp(feature, "blind")) {
2480 		blind = 1;
2481 	}
2482 
2483 	if(NULL == psdoc->font) {
2484 		ps_error(psdoc, PS_RuntimeError, _("Cannot output text before setting a font."));
2485 		return 0;
2486 	}
2487 
2488 	if(NULL == psdoc->font->metrics) {
2489 		ps_error(psdoc, PS_RuntimeError, _("No font metrics loaded."));
2490 		return 0;
2491 	}
2492 	if(!(fontid = _ps_find_font(psdoc, psdoc->font))) {
2493 		ps_error(psdoc, PS_RuntimeError, _("Could not find font resource."));
2494 		return 0;
2495 	}
2496 
2497 	hyphenationonoff = ps_get_bool_parameter(psdoc, "hyphenation", 0);
2498 	if(hyphenationonoff) {
2499 		if(!psdoc->hdict) {
2500 			ps_error(psdoc, PS_Warning, _("No hyphenation table set, turning hyphenation off."));
2501 			hyphenationonoff = ps_false;
2502 		} else {
2503 			if(0 == (hyphenminchars = (int) PS_get_value(psdoc, "hyphenminchars", 0)))
2504 				hyphenminchars = 3;
2505 		}
2506 	}
2507 
2508 	/* If CR is treated as line break, it may not be treated as
2509 	 * parbreak as well.
2510 	 */
2511 	treatcrasspace = !ps_get_bool_parameter(psdoc, "linebreak", 0);
2512 	if(treatcrasspace)
2513 		treatcraspar = ps_get_bool_parameter(psdoc, "parbreak", 1);
2514 	else
2515 		treatcraspar = 0;
2516 
2517 	/* Calculate baseline of first line in box */
2518 	ypos = bottom + height - psdoc->font->metrics->ascender*psdoc->font->size/1000.0;
2519 	textcopy = ps_strdup(psdoc, text);
2520 	str = textcopy;
2521 	curpos = 0;
2522 	lastbreak = 0;
2523 	lastdelim = 0;
2524 	vlen = 0;
2525 	firstword = 1;
2526 	boxlinecounter = 0;
2527 	parlinecounter = 0;
2528 	parcounter = 0;
2529 	if((leading = PS_get_value(psdoc, "leading", 0)) <= 0.0)
2530 		leading = (psdoc->font->metrics->ascender - psdoc->font->metrics->descender)*psdoc->font->size*1.2/1000.0;
2531 	if((parindent = PS_get_value(psdoc, "parindent", 0)) <= 0.0)
2532 		parindent = 0.0;
2533 	else {
2534 		if((numindentlines = (int) PS_get_value(psdoc, "numindentlines", 0.0)) <= 0)
2535 			numindentlines = 1;
2536 	}
2537 	if((parskip = PS_get_value(psdoc, "parskip", 0)) <= 0.0)
2538 		parskip = 0.0;
2539 	if((parindentskip = (int) PS_get_value(psdoc, "parindentskip", 0.0)) <= 0)
2540 		parindentskip = 0;
2541 	linecounter = NULL;
2542 	if((linenumbermode = PS_get_parameter(psdoc, "linenumbermode", 0.0)) != NULL) {
2543 		if(0 == strcmp(linenumbermode, "paragraph"))
2544 			linecounter = &parlinecounter;
2545 		else if(0 == strcmp(linenumbermode, "box"))
2546 			linecounter = &boxlinecounter;
2547 		else
2548 			ps_error(psdoc, PS_Warning, _("Unknown line number mode '%s'. Turning line numbering off."), linenumbermode);
2549 		if(linecounter) {
2550 			if((linenumberspace = PS_get_value(psdoc, "linenumberspace", 0.0)) <= 0.0)
2551 				linenumberspace = 20;
2552 			if((linenumbersep = PS_get_value(psdoc, "linenumbersep", 0.0)) <= 0.0)
2553 				linenumbersep = 5;
2554 			width -= (linenumberspace+linenumbersep);
2555 			left += (linenumberspace+linenumbersep);
2556 
2557 		}
2558 	}
2559 	old_word_spacing = ps_get_word_spacing(psdoc, psdoc->font);
2560 	oldypos = ypos;
2561 	/* Output text until all shown or the box is full */
2562 	while(*str != '\0' && (ypos >= bottom || height == 0.0)) {
2563 		/* reset length of line */
2564 		hlen = 0.0;
2565 		/* Set normal word spacing for the current font */
2566 		ps_set_word_spacing(psdoc, psdoc->font, 0.0);
2567 		spaces = 0;
2568 		firstword = 1;
2569 		lineend = ps_false;
2570 		parend = ps_false;
2571 		linewidth = width;
2572 		/* Indent only the first n lines of paragraph, but only starting
2573 		 * with th m'th paragraph
2574 		 */
2575 		doindent = parlinecounter < numindentlines && parcounter >= parindentskip;
2576 		if(doindent) {
2577 			linewidth -= parindent;
2578 		}
2579 		/* Collect text until line is full, line has been ended or no more chars */
2580 		while(hlen < linewidth && *str != '\0' && lineend == ps_false && parend == ps_false) {
2581 			/* Search for next word boundry (delimiter) */
2582 			while(*str != ' ' && *str != '-' && *str != '\n' && *str != '\r' && *str != '�' && *str != '\0') {
2583 				prevchar = *str;
2584 				str++;
2585 				curpos++;
2586 			}
2587 
2588 			/* Treat '\n' and '\r' as space */
2589 			if(*str == '\n' || *str == '\r') {
2590 				if(!treatcrasspace && *str == '\n') {
2591 					lineend = ps_true;
2592 				}
2593 				if(treatcraspar && prevchar == '\n' && *str == '\n') {
2594 					parend = ps_true;
2595 				}
2596 			} else if(*str == '\t') {
2597 				*str = ' ';
2598 			}
2599 
2600 			/* a space at the end of the line has to be skipped, but a '-' or hyphen
2601 			 * must be displayed and taken into account for stringwidth.
2602 			 * It could be that there are other words in this line and the
2603 			 * current char will be the last in the line. For that reason morechars
2604 			 * is set 0 for each new word boundry which isn't a '-' or a hyphen. */
2605 			if(parend) {
2606 				morechars = -1;
2607 			} else if(*str == '-') {
2608 				morechars = 1;
2609 			} else {
2610 				morechars = 0;
2611 			}
2612 			/* Calculate the width of the complete line from the last break to
2613 			 * the current position. Save the old len before, just in case
2614 			 * the line is too long and we need the old value.
2615 			 */
2616 			oldhlen = hlen;
2617 			hlen = PS_stringwidth2(psdoc, &textcopy[lastbreak], curpos-lastbreak+morechars, fontid, psdoc->font->size);
2618 			//printf("'%s' (%d) ist %f lang\n", &textcopy[lastbreak], curpos-lastbreak+morechars, hlen);
2619 			/* Is the line length still smaller or is this the first word which
2620 			 * always is displayed even if it ecxeeds the line width.
2621 			 */
2622 			if(hlen < linewidth || firstword == 1) {
2623 //				printf("%f ist noch kleiner als %f\n", hlen, width);
2624 				lastdelim = curpos;
2625 				prevchar = *str;
2626 
2627 				/* CR in the middle of the line will be made to spaces */
2628 				if(*str == '\n')
2629 					*str = ' ';
2630 
2631 				/* Count space for later calculation of spacing between words. Will
2632 				 * be corrected if this is the last space of a line.
2633 				 */
2634 				if(*str == ' ')
2635 					spaces++;
2636 
2637 				/* We have passed the first word */
2638 				firstword = 0;
2639 
2640 				/* If you are not at the end of the string then move to
2641 				 * the next char and skip the delimiter. We need to do
2642 				 * that only if the line isn't full yet, otherwise
2643 				 * we will drop out of the while loop anyway. */
2644 				if(*str != '\0') {
2645 					str++;
2646 					curpos++;
2647 				}
2648 			}
2649 		}
2650 		/* We have dropped the while loop because the text ended before the
2651 		 * line was full, the line was ended by cr or the line is overfilled.
2652 		 * curpos and str now point to the next delimiter. The line does not
2653 		 * contain cr anymore. They has been replaced by spaces. */
2654 
2655 		/* Do not take the last space in a line into account */
2656 		if(textcopy[lastdelim] == ' ')
2657 			spaces--;
2658 
2659 		/* morechars may not have the right value, because it has been set for
2660 		 * the last character read, which may not be the character at index
2661 		 * lastdelim. This happens if a word does not fit into the line anymore.
2662 		 */
2663 		if(parend) {
2664 			morechars = -1;
2665 		} else if(textcopy[lastdelim] != ' ' && textcopy[lastdelim] != '\0') {
2666 			morechars = 1;
2667 		} else {
2668 			morechars = 0;
2669 		}
2670 //		printf("lastdelim-1 = '%c', curpos-1 = '%c', morechars = %d\n", textcopy[lastdelim-1], textcopy[curpos-1], morechars);
2671 //		printf("output line '%s' (%d) has %d spaces\n", &textcopy[lastbreak], lastdelim-lastbreak+morechars, spaces);
2672 //
2673 		/* The word between lastdelim and curpos should now be hyphenated.
2674 		 * No need for hyphenation if there is no more text or the line
2675 		 * has been ended for some reason (e.g. a cr which was not treated
2676 		 * as space).
2677 		 */
2678 		if(!lineend && !parend && textcopy[lastdelim] != '\0' && hyphenationonoff) {
2679 			char *hyphenword = (char *) ps_calloc(psdoc, (curpos-lastdelim+3)*sizeof(char), _("Could not allocated memory for hyphenated word."));
2680 //			printf("Remaining space in line is %.2f\n", linewidth-oldhlen);
2681 			if(NULL != hyphenword) {
2682 				int i, k, lasthp;
2683 				float lenhp = 0.0;
2684 
2685 				/* Just consider the last word in the line. It starts with
2686 				 * the last delimiter which can be ' ' or '-' or a hyphen and
2687 				 * ends it the end of the word. Since curpos points to the next
2688 				 * delimiter we are set.
2689 				 */
2690 				/* Same situation as with morechars before. If the delimiter is
2691 				 * not a space or '\0' we have to take it into account.
2692 				 * Actually it doesn't matter if we take a hyphen into account
2693 				 * or not, but maybe there is a difference between 'word' and
2694 				 * 'word-' if it is hyphenated.
2695 				 */
2696 				if(textcopy[curpos] != ' ' && textcopy[curpos] != '\n' && textcopy[curpos] != '\0') {
2697 					strncpy(hyphenword, &textcopy[lastdelim+1], curpos-lastdelim-morechars+1);
2698 				} else {
2699 					strncpy(hyphenword, &textcopy[lastdelim+1], curpos-lastdelim-morechars);
2700 				}
2701 
2702 				lasthp = 0;
2703 				/* hyphenate only words with at least 2*hyphenminchars chars
2704 				 * All chars at the beginning of the word which are not alpha
2705 				 * will be counted and later taken into account.
2706 				 */
2707 				k = 0;
2708 				while(hyphenword[k] && !isalpha(hyphenword[k]))
2709 					k++;
2710 				if((strlen(hyphenword)-k) > 2*hyphenminchars) {
2711 					char *buffer;
2712 					char buf1[100];
2713 					int l;
2714 					/* buf1 will be the partial hyphenated word including all not
2715 					 * alpha chars at the beginning of the word. This string will
2716 					 * be used to calculate the len of the line.
2717 					 */
2718 					buffer = (char*) psdoc->malloc(psdoc, sizeof(char) * (strlen(hyphenword)+3), _("Could not allocate memory for hyphenation buffer."));
2719 					hnj_hyphen_hyphenate(psdoc->hdict, &hyphenword[k], strlen(&hyphenword[k]), buffer);
2720 //					printf("Word to hyphenate: '%s'\n", &hyphenword[k]);
2721 //					printf("                    %s\n", buffer);
2722 					buf1[0] = textcopy[lastdelim];
2723 					k++;
2724 					for(l=1; l<k; l++) {
2725 						buf1[l] = textcopy[lastdelim+1];
2726 					}
2727 					for(i=hyphenminchars-1, hlen=0; (i < strlen(&hyphenword[k-1])-hyphenminchars) && (hlen < (linewidth-oldhlen)); i++) {
2728 	//					printf("%c", hyphenword[i]);
2729 						if(buffer[i] & 1) {
2730 							strncpy(&buf1[k], &hyphenword[k-1], i+1);
2731 							buf1[i+k+1] = '�';
2732 							buf1[i+k+2] = '\0';
2733 //							printf("buf1 = %s\n", buf1);
2734 							hlen = PS_stringwidth2(psdoc, buf1, -1, fontid, psdoc->font->size);
2735 							if(hlen < (linewidth-oldhlen)) {
2736 								lasthp = i+k;
2737 								lenhp = hlen;
2738 							}
2739 //							printf("Len of %s (%d) is %.2f (%.2f)\n", buf1, i+1, hlen, linewidth-oldhlen);
2740 	//						printf("-");
2741 						}
2742 					}
2743 //					printf("Hyphenation at char %d\n", lasthp+1);
2744 					if(lasthp > 0) {
2745 						if(textcopy[lastdelim] == ' ') {
2746 							spaces++;
2747 						}
2748 						oldhlen += lenhp;
2749 						lastdelim += lasthp;
2750 						morechars = 1;
2751 					}
2752 					psdoc->free(psdoc, buffer);
2753 				}
2754 				/* At this point we know a possible hyphenation postition. The
2755 				 * next step would be to take the line concat it with the hyphenated
2756 				 * word, determine its length, correct curpos, lastdelim, oldhlen
2757 				 * and str
2758 				 */
2759 
2760 				psdoc->free(psdoc, hyphenword);
2761 
2762 				if(NULL != (linebuf = psdoc->malloc(psdoc, lastdelim-lastbreak+morechars+2, _("Could not allocate memory for line buffer.")))) {
2763 					strncpy(linebuf, &textcopy[lastbreak], lastdelim-lastbreak+morechars);
2764 					if(lasthp > 0)
2765 						linebuf[lastdelim-lastbreak+morechars] = '�';
2766 					else
2767 						linebuf[lastdelim-lastbreak+morechars] = '\0';
2768 					linebuf[lastdelim-lastbreak+morechars+1] = '\0';
2769 				} else {
2770 					return 0;
2771 				}
2772 			}
2773 		} else {
2774 			if(NULL != (linebuf = psdoc->malloc(psdoc, lastdelim-lastbreak+morechars+1, _("Could not allocate memory for line buffer.")))) {
2775 				strncpy(linebuf, &textcopy[lastbreak], lastdelim-lastbreak+morechars);
2776 				linebuf[lastdelim-lastbreak+morechars] = '\0';
2777 			} else {
2778 				return 0;
2779 			}
2780 		}
2781 
2782 //		printf("Complete line: '%s'\n", linebuf);
2783 //		printf("Line has %d spaces\n", spaces);
2784 //		printf("Line is %f (%f) long\n\n", PS_stringwidth2(psdoc, linebuf, strlen(linebuf), psdoc->font, psdoc->font->size), oldhlen);
2785 
2786 		if(!blind) {
2787 			/* Recalculate the length of the line, althought oldhlen should contain
2788 			 * the proper length already, it differs from the len calculated by
2789 			 * the following line FIXME: must be investigated
2790 			 */
2791 			oldhlen = PS_stringwidth2(psdoc, linebuf, -1, fontid, psdoc->font->size);
2792 
2793 			xpos = 0.0;
2794 			switch(mode) {
2795 				case PS_TEXT_HMODE_FULLJUSTIFY:
2796 				case PS_TEXT_HMODE_JUSTIFY: {
2797 					/* If the paragraph was ended e.g. by a cr, we will not care about
2798 					 * PS_TEXT_HMODE_JUSTIFY. */
2799 					if(parend) {
2800 						xpos = left;
2801 					} else {
2802 						float extraleftspace = 0.0;
2803 						float extrarightspace = 0.0;
2804 						if(spaces != 0 && *str != '\0' && textcopy[lastdelim] != '\n') {
2805 							ADOBEINFO *ai;
2806 							unsigned char singlechar;
2807 
2808 							/* check if the last char of the line may protrude into the right
2809 							 * margin. */
2810 							singlechar = (unsigned char) linebuf[strlen(linebuf)-1];
2811 		//					printf("Last char of line is %c\n", singlechar);
2812 							if(NULL != (ai = gfindadobe(psdoc->font->metrics->gadobechars, ps_inputenc_name(psdoc, singlechar)))) {
2813 			//					printf("RightMarginKerning for '%s' is %d\n",  ps_inputenc_name(psdoc, singlechar), ai->rkern);
2814 								extrarightspace = ai->width*psdoc->font->size*ai->rkern/1000000.0;
2815 							}
2816 
2817 							/* check if the first char of the line may protrude into the left
2818 							 * margin. */
2819 							singlechar = (unsigned char) linebuf[0];
2820 		//					printf("First char of line is %c\n", singlechar);
2821 							if(NULL != (ai = gfindadobe(psdoc->font->metrics->gadobechars, ps_inputenc_name(psdoc, singlechar)))) {
2822 			//					printf("RightMarginKerning for '%s' is %d\n",  ps_inputenc_name(psdoc, singlechar), ai->rkern);
2823 								extraleftspace = ai->width*psdoc->font->size*ai->lkern/1000000.0;
2824 							}
2825 		//					printf("Set word spacing to %f\n", (linewidth-oldhlen)/spaces);
2826 							if((mode == PS_TEXT_HMODE_FULLJUSTIFY) || (ypos-leading >= bottom))
2827 								ps_add_word_spacing(psdoc, psdoc->font, (linewidth+extraleftspace+extrarightspace-oldhlen)/(float)spaces);
2828 						}
2829 						/* set oldhlen to ensure texty is set properly */
2830 						oldhlen = linewidth;
2831 						xpos = left-extraleftspace;
2832 					}
2833 					if(doindent) {
2834 						xpos += parindent;
2835 					}
2836 					break;
2837 				}
2838 				case PS_TEXT_HMODE_LEFT:
2839 					/* FIXME: consider left margin kerning, see extraleftspace
2840 					 * as used above */
2841 					xpos = left;
2842 					if(doindent) {
2843 						xpos += parindent;
2844 					}
2845 					break;
2846 				case PS_TEXT_HMODE_RIGHT:
2847 					xpos = left + (linewidth-oldhlen);
2848 					break;
2849 				case PS_TEXT_HMODE_CENTER:
2850 					xpos = left + (linewidth-oldhlen)/2;
2851 					break;
2852 			}
2853 
2854 			PS_show_xy2(psdoc, linebuf, strlen(linebuf), xpos, ypos);
2855 			if(linecounter) {
2856 				char linenumstr[11];
2857 #ifdef HAVE_SNPRINTF
2858 				snprintf(linenumstr, 10, "%d", *linecounter+1);
2859 #else
2860 				sprintf(linenumstr, "%d", *linecounter+1);
2861 #endif
2862 				PS_show_xy2(psdoc, linenumstr, strlen(linenumstr), left-linenumbersep-PS_stringwidth2(psdoc, linenumstr, -1, fontid, psdoc->font->size), ypos);
2863 			}
2864 		}
2865 		psdoc->free(psdoc, linebuf);
2866 
2867 //		printf("lastdelim = %c (%d), lastbreak = %d\n", textcopy[lastdelim], lastdelim, lastbreak);
2868 		/* Set the string position to 1 char after the last delimiter */
2869 		if(textcopy[lastdelim] != '\0')
2870 			curpos = lastdelim+1;
2871 		str = &textcopy[curpos];
2872 		lastbreak = lastdelim+1;
2873 		/* forward one line by leading */
2874 		oldypos = ypos;
2875 		ypos = ypos - leading;
2876 		boxlinecounter++;
2877 		if(parend) {
2878 			ypos -= parskip;
2879 			parlinecounter = 0;
2880 			parcounter++;
2881 		} else {
2882 			parlinecounter++;
2883 		}
2884 	}
2885 	psdoc->free(psdoc, textcopy);
2886 
2887 	/* Set new text position. textx may not be set, because it has been set by
2888 	 * PS_show_xy2() already.
2889 	 */
2890 	if(!blind)
2891 		psdoc->tstates[psdoc->tstate].ty = oldypos;
2892 	PS_set_value(psdoc, "boxheight", (bottom + height) - (oldypos - leading + psdoc->font->metrics->ascender*psdoc->font->size/1000.0));
2893 //	printf("Reset word spacing to %f\n", old_word_spacing);
2894 	ps_set_word_spacing(psdoc, psdoc->font, old_word_spacing);
2895 	if(text[curpos] == '\0')
2896 		return 0;
2897 	else
2898 		return(ps_strlen(text) - curpos);
2899 }
2900 /* }}} */
2901 
2902 /* PS_moveto() {{{
2903  * Moves the current position (cursor) to the passed position
2904  * This function starts a new path if it is called in page-scope,
2905  * otherwise it will set the current point and continues with the
2906  * current path.
2907  */
2908 PSLIB_API void PSLIB_CALL
PS_moveto(PSDoc * psdoc,float x,float y)2909 PS_moveto(PSDoc *psdoc, float x, float y) {
2910 	if(NULL == psdoc) {
2911 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
2912 		return;
2913 	}
2914 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH | PS_SCOPE_PAGE | PS_SCOPE_TEMPLATE | PS_SCOPE_PATTERN | PS_SCOPE_GLYPH)) {
2915 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path', 'template', 'pattern', 'glyph' or 'page' scope."), __FUNCTION__);
2916 		return;
2917 	}
2918 	psdoc->agstates[psdoc->agstate].x = x;
2919 	psdoc->agstates[psdoc->agstate].y = y;
2920 	/* Only set the scope to PS_SCOPE_PATH if we are not already in that
2921 	 * scope.
2922 	 */
2923 	if(ps_current_scope(psdoc) != PS_SCOPE_PATH) {
2924 		ps_enter_scope(psdoc, PS_SCOPE_PATH);
2925 		ps_printf(psdoc, "newpath\n");
2926 	}
2927 	ps_printf(psdoc, "%.2f %.2f a\n", x, y);
2928 }
2929 /* }}} */
2930 
2931 /* PS_show_xy() {{{
2932  * Calls PS_moveto and PS_show to print the text on the selected position
2933  */
2934 PSLIB_API void PSLIB_CALL
PS_show_xy(PSDoc * psdoc,const char * text,float x,float y)2935 PS_show_xy(PSDoc *psdoc, const char *text, float x, float y) {
2936 	PS_show_xy2(psdoc, text, 0, x, y);
2937 }
2938 /* }}} */
2939 
2940 /* PS_show_xy2() {{{
2941  * Calls PS_moveto and PS_show2 to print the text on the selected position
2942  */
2943 PSLIB_API void PSLIB_CALL
PS_show_xy2(PSDoc * psdoc,const char * text,int xlen,float x,float y)2944 PS_show_xy2(PSDoc *psdoc, const char *text, int xlen, float x, float y) {
2945 	if(NULL == psdoc) {
2946 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
2947 		return;
2948 	}
2949 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
2950 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
2951 		return;
2952 	}
2953 	psdoc->tstates[psdoc->tstate].tx = x;
2954 	psdoc->tstates[psdoc->tstate].cx = x;
2955 	psdoc->tstates[psdoc->tstate].ty = y;
2956 	psdoc->tstates[psdoc->tstate].cy = y;
2957 	PS_show2(psdoc, text, xlen);
2958 }
2959 /* }}} */
2960 
2961 /* PS_lineto() {{{
2962  * Draws a line from the current point to the passed point
2963  */
2964 PSLIB_API void PSLIB_CALL
PS_lineto(PSDoc * psdoc,float x,float y)2965 PS_lineto(PSDoc *psdoc, float x, float y) {
2966 	if(NULL == psdoc) {
2967 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
2968 		return;
2969 	}
2970 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH)) {
2971 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path' scope."), __FUNCTION__);
2972 		return;
2973 	}
2974 	psdoc->agstates[psdoc->agstate].x = x;
2975 	psdoc->agstates[psdoc->agstate].y = y;
2976 	ps_printf(psdoc, "%.2f %.2f l\n", x, y);
2977 }
2978 /* }}} */
2979 
2980 /* PS_set_text_pos() {{{
2981  * Set the coordinates for the next text output.
2982  */
2983 PSLIB_API void PSLIB_CALL
PS_set_text_pos(PSDoc * psdoc,float x,float y)2984 PS_set_text_pos(PSDoc *psdoc, float x, float y) {
2985 	if(NULL == psdoc) {
2986 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
2987 		return;
2988 	}
2989 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
2990 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
2991 		return;
2992 	}
2993 	psdoc->tstates[psdoc->tstate].tx = x;
2994 	psdoc->tstates[psdoc->tstate].ty = y;
2995 	psdoc->tstates[psdoc->tstate].cx = x;
2996 	psdoc->tstates[psdoc->tstate].cy = y;
2997 }
2998 /* }}} */
2999 
3000 /* PS_setlinewidth() {{{
3001  * Set the width of a line for all drawing operations.
3002  */
3003 PSLIB_API void PSLIB_CALL
PS_setlinewidth(PSDoc * p,float width)3004 PS_setlinewidth(PSDoc *p, float width) {
3005 	if(NULL == p) {
3006 		ps_error(p, PS_RuntimeError, _("PSDoc is null."));
3007 		return;
3008 	}
3009 	if(!ps_check_scope(p, PS_SCOPE_PAGE | PS_SCOPE_TEMPLATE | PS_SCOPE_PATTERN | PS_SCOPE_GLYPH)) {
3010 		ps_error(p, PS_RuntimeError, _("%s must be called within 'page', 'template', 'glyph', or 'pattern' scope."), __FUNCTION__);
3011 		return;
3012 	}
3013 	ps_printf(p, "%f setlinewidth\n", width);
3014 }
3015 /* }}} */
3016 
3017 /* PS_setlinecap() {{{
3018  * Sets the appearance of the line ends.
3019  */
3020 PSLIB_API void PSLIB_CALL
PS_setlinecap(PSDoc * p,int type)3021 PS_setlinecap(PSDoc *p, int type) {
3022 	if(NULL == p) {
3023 		ps_error(p, PS_RuntimeError, _("PSDoc is null."));
3024 		return;
3025 	}
3026 	if(!ps_check_scope(p, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3027 		ps_error(p, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3028 		return;
3029 	}
3030 	if((type > PS_LINECAP_SQUARED) || (type < PS_LINECAP_BUTT)) {
3031 		ps_error(p, PS_Warning, _("Type of linecap is out of range."));
3032 		return;
3033 	}
3034 	ps_printf(p, "%d setlinecap\n", type);
3035 }
3036 /* }}} */
3037 
3038 /* PS_setlinejoin() {{{
3039  * Sets how lines are joined in a polygon.
3040  */
3041 PSLIB_API void PSLIB_CALL
PS_setlinejoin(PSDoc * p,int type)3042 PS_setlinejoin(PSDoc *p, int type) {
3043 	if(NULL == p) {
3044 		ps_error(p, PS_RuntimeError, _("PSDoc is null."));
3045 		return;
3046 	}
3047 	if(!ps_check_scope(p, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3048 		ps_error(p, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3049 		return;
3050 	}
3051 	if((type > PS_LINEJOIN_BEVEL) || (type < PS_LINEJOIN_MITER)) {
3052 		ps_error(p, PS_Warning, _("Type of linejoin is out of range."));
3053 		return;
3054 	}
3055 	ps_printf(p, "%d setlinejoin\n", type);
3056 }
3057 /* }}} */
3058 
3059 /* PS_setmiterlimit() {{{
3060  */
3061 PSLIB_API void PSLIB_CALL
PS_setmiterlimit(PSDoc * p,float value)3062 PS_setmiterlimit(PSDoc *p, float value) {
3063 	if(NULL == p) {
3064 		ps_error(p, PS_RuntimeError, _("PSDoc is null."));
3065 		return;
3066 	}
3067 	if(!ps_check_scope(p, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3068 		ps_error(p, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3069 		return;
3070 	}
3071 	if(value < 1) {
3072 		ps_error(p, PS_Warning, _("Miter limit is less than 1."));
3073 		return;
3074 	}
3075 	ps_printf(p, "%f setmiterlimit\n", value);
3076 }
3077 /* }}} */
3078 
3079 /* PS_setflat() {{{
3080  */
3081 PSLIB_API void PSLIB_CALL
PS_setflat(PSDoc * p,float value)3082 PS_setflat(PSDoc *p, float value) {
3083 	if(NULL == p) {
3084 		ps_error(p, PS_RuntimeError, _("PSDoc is null."));
3085 		return;
3086 	}
3087 	if(!ps_check_scope(p, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3088 		ps_error(p, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3089 		return;
3090 	}
3091 	if(value < 0.2 || value > 100.0) {
3092 		ps_error(p, PS_Warning, _("Flat value is less than 0.2 or bigger than 100.0"));
3093 		return;
3094 	}
3095 	ps_printf(p, "%f setflat\n", value);
3096 }
3097 /* }}} */
3098 
3099 /* PS_setdash() {{{
3100  * Sets the length of the black and white portions of a dashed line.
3101  */
3102 PSLIB_API void PSLIB_CALL
PS_setdash(PSDoc * p,float on,float off)3103 PS_setdash(PSDoc *p, float on, float off) {
3104 	if(NULL == p) {
3105 		ps_error(p, PS_RuntimeError, _("PSDoc is null."));
3106 		return;
3107 	}
3108 	if(!ps_check_scope(p, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3109 		ps_error(p, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3110 		return;
3111 	}
3112 	if(on == 0.0 && off == 0.0) {
3113 		ps_printf(p, "[] 0 setdash\n");
3114 	} else {
3115 		ps_printf(p, "[%f %f] 0 setdash\n", on, off);
3116 	}
3117 }
3118 /* }}} */
3119 
3120 /* PS_setpolydash() {{{
3121  * Sets a more complicated dash line with. arr is list of black and white
3122  * portions.
3123  */
3124 PSLIB_API void PSLIB_CALL
PS_setpolydash(PSDoc * p,float * arr,int length)3125 PS_setpolydash(PSDoc *p, float *arr, int length) {
3126 	float *dash;
3127 	int i;
3128 
3129 	if(NULL == p) {
3130 		ps_error(p, PS_RuntimeError, _("PSDoc is null."));
3131 		return;
3132 	}
3133 	if(!ps_check_scope(p, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3134 		ps_error(p, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3135 		return;
3136 	}
3137 	if(NULL == arr) {
3138 		ps_error(p, PS_RuntimeError, _("Array for dashes is NULL."));
3139 		return;
3140 	}
3141 
3142 	ps_printf(p, "[");
3143 	dash = arr;
3144 	for(i=0; i<length; i++, dash++) {
3145 		ps_printf(p, "%f ", *dash);
3146 	}
3147 	ps_printf(p, "] 0 setdash\n");
3148 }
3149 /* }}} */
3150 
3151 /* PS_setmatrix() {{{
3152  * FIXME: Not implemented
3153  */
3154 PSLIB_API void PSLIB_CALL
PS_setmatrix(PSDoc * p,float a,float b,float c,float d,float e,float f)3155 PS_setmatrix(PSDoc *p, float a, float b, float c, float d, float e, float f) {
3156 	if(NULL == p) {
3157 		ps_error(p, PS_RuntimeError, _("PSDoc is null."));
3158 		return;
3159 	}
3160 	if(!ps_check_scope(p, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3161 		ps_error(p, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3162 		return;
3163 	}
3164 }
3165 /* }}} */
3166 
3167 /* PS_setoverprintmode() {{{
3168  * Sets overprint flag true or false.
3169  */
3170 PSLIB_API void PSLIB_CALL
PS_setoverprintmode(PSDoc * p,int mode)3171 PS_setoverprintmode(PSDoc *p, int mode) {
3172 	if(NULL == p) {
3173 		ps_error(p, PS_RuntimeError, _("PSDoc is null."));
3174 		return;
3175 	}
3176 	if(!ps_check_scope(p, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3177 		ps_error(p, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3178 		return;
3179 	}
3180 	if((mode > 1) || (mode < 0)) {
3181 		ps_error(p, PS_Warning, _("Mode for overprint must be either 0 or 1."));
3182 		return;
3183 	}
3184 	ps_printf(p, "%s setoverprint\n", mode == 0 ? "false" : "true");
3185 }
3186 /* }}} */
3187 
3188 /* PS_setsmoothness() {{{
3189  * Sets smoothness.
3190  */
3191 PSLIB_API void PSLIB_CALL
PS_setsmoothness(PSDoc * p,float smoothness)3192 PS_setsmoothness(PSDoc *p, float smoothness) {
3193 	if(NULL == p) {
3194 		ps_error(p, PS_RuntimeError, _("PSDoc is null."));
3195 		return;
3196 	}
3197 	if(!ps_check_scope(p, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3198 		ps_error(p, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3199 		return;
3200 	}
3201 	if((smoothness > 1) || (smoothness < 0)) {
3202 		ps_error(p, PS_Warning, _("Smoothness value must be between 0 and 1."));
3203 		return;
3204 	}
3205 	ps_printf(p, "%.4f setsmoothness\n", smoothness);
3206 }
3207 /* }}} */
3208 
3209 /* PS_rect() {{{
3210  * Draws a rectangle. The rectancle is either added to the current path
3211  * or starts a new one.
3212  */
3213 PSLIB_API void PSLIB_CALL
PS_rect(PSDoc * psdoc,float x,float y,float width,float height)3214 PS_rect(PSDoc *psdoc, float x, float y, float width, float height) {
3215 	if(NULL == psdoc) {
3216 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3217 		return;
3218 	}
3219 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH | PS_SCOPE_PAGE | PS_SCOPE_TEMPLATE | PS_SCOPE_PATTERN | PS_SCOPE_GLYPH)) {
3220 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path', 'template', 'pattern', 'glyph' or 'page' scope."), __FUNCTION__);
3221 		return;
3222 	}
3223 	psdoc->agstates[psdoc->agstate].x = x;
3224 	psdoc->agstates[psdoc->agstate].y = y;
3225 	if(ps_current_scope(psdoc) != PS_SCOPE_PATH) {
3226 		ps_enter_scope(psdoc, PS_SCOPE_PATH);
3227 		ps_printf(psdoc, "newpath\n");
3228 	}
3229 	ps_printf(psdoc, "%.4f %.4f a\n", x, y);
3230 	ps_printf(psdoc, "%.4f %.4f l\n", x+width, y);
3231 	ps_printf(psdoc, "%.4f %.4f l\n", x+width, y+height);
3232 	ps_printf(psdoc, "%.4f %.4f l\n", x, y+height);
3233 	ps_printf(psdoc, "closepath\n");
3234 }
3235 /* }}} */
3236 
3237 /* PS_circle() {{{
3238  * Draws a circle
3239  */
3240 PSLIB_API void PSLIB_CALL
PS_circle(PSDoc * psdoc,float x,float y,float radius)3241 PS_circle(PSDoc *psdoc, float x, float y, float radius) {
3242 	if(NULL == psdoc) {
3243 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3244 		return;
3245 	}
3246 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH | PS_SCOPE_PAGE | PS_SCOPE_TEMPLATE | PS_SCOPE_PATTERN | PS_SCOPE_GLYPH)) {
3247 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path', 'template', 'pattern', 'glyph' or 'page' scope."), __FUNCTION__);
3248 		return;
3249 	}
3250 	if(radius < 0.0) {
3251 		ps_error(psdoc, PS_RuntimeError, _("Radius for circle is less than 0.0."));
3252 		return;
3253 	}
3254 
3255 	psdoc->agstates[psdoc->agstate].x = x;
3256 	psdoc->agstates[psdoc->agstate].y = y;
3257 	if(ps_current_scope(psdoc) != PS_SCOPE_PATH) {
3258 		ps_enter_scope(psdoc, PS_SCOPE_PATH);
3259 		ps_printf(psdoc, "newpath\n");
3260 	}
3261 	ps_printf(psdoc, "%.4f %.4f a\n", x+radius, y);
3262 	ps_printf(psdoc, "%.4f %.4f %.4f 0 360 arc\n", x, y, radius);
3263 }
3264 /* }}} */
3265 
3266 /* PS_arc() {{{
3267  * Draws an arc counterclockwise
3268  */
3269 PSLIB_API void PSLIB_CALL
PS_arc(PSDoc * psdoc,float x,float y,float radius,float alpha,float beta)3270 PS_arc(PSDoc *psdoc, float x, float y, float radius, float alpha, float beta) {
3271 	if(NULL == psdoc) {
3272 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3273 		return;
3274 	}
3275 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH | PS_SCOPE_PAGE | PS_SCOPE_TEMPLATE | PS_SCOPE_PATTERN)) {
3276 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path', 'template', 'pattern' or 'page' scope."), __FUNCTION__);
3277 		return;
3278 	}
3279 	if(radius < 0.0) {
3280 		ps_error(psdoc, PS_RuntimeError, _("Radius for arc is less than 0.0."));
3281 		return;
3282 	}
3283 
3284 	psdoc->agstates[psdoc->agstate].x = x;
3285 	psdoc->agstates[psdoc->agstate].y = y;
3286 	if(ps_current_scope(psdoc) != PS_SCOPE_PATH) {
3287 		ps_enter_scope(psdoc, PS_SCOPE_PATH);
3288 		ps_printf(psdoc, "newpath\n");
3289 	}
3290 //	ps_printf(psdoc, "%.4f %.4f a\n", x+radius*cos(alpha/180*M_PI), y+radius*sin(alpha/180*M_PI));
3291 	ps_printf(psdoc, "%.4f %.4f %.4f %.4f %.4f arc\n", x, y, radius, alpha, beta);
3292 }
3293 /* }}} */
3294 
3295 /* PS_arcn() {{{
3296  * Draws an arc clockwise
3297  */
3298 PSLIB_API void PSLIB_CALL
PS_arcn(PSDoc * psdoc,float x,float y,float radius,float alpha,float beta)3299 PS_arcn(PSDoc *psdoc, float x, float y, float radius, float alpha, float beta) {
3300 	if(NULL == psdoc) {
3301 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3302 		return;
3303 	}
3304 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH | PS_SCOPE_PAGE | PS_SCOPE_TEMPLATE | PS_SCOPE_PATTERN)) {
3305 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path', 'template', 'pattern' or 'page' scope."), __FUNCTION__);
3306 		return;
3307 	}
3308 	if(radius < 0.0) {
3309 		ps_error(psdoc, PS_RuntimeError, _("Radius for arc is less than 0.0."));
3310 		return;
3311 	}
3312 
3313 	psdoc->agstates[psdoc->agstate].x = x;
3314 	psdoc->agstates[psdoc->agstate].y = y;
3315 	if(ps_current_scope(psdoc) != PS_SCOPE_PATH) {
3316 		ps_enter_scope(psdoc, PS_SCOPE_PATH);
3317 		ps_printf(psdoc, "newpath\n");
3318 	}
3319 //	ps_printf(psdoc, "%.4f %.4f a\n", x+radius*cos(beta/180*M_PI), y+radius*sin(beta/180*M_PI));
3320 	ps_printf(psdoc, "%.4f %.4f %.4f %.4f %.4f arcn\n", x, y, radius, alpha, beta);
3321 }
3322 /* }}} */
3323 
3324 /* PS_curveto() {{{
3325  * Draws a curve
3326  */
3327 PSLIB_API void PSLIB_CALL
PS_curveto(PSDoc * psdoc,float x1,float y1,float x2,float y2,float x3,float y3)3328 PS_curveto(PSDoc *psdoc, float x1, float y1, float x2, float y2, float x3, float y3) {
3329 	if(NULL == psdoc) {
3330 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3331 		return;
3332 	}
3333 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH)) {
3334 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path' scope."), __FUNCTION__);
3335 		return;
3336 	}
3337 	psdoc->agstates[psdoc->agstate].x = x3;
3338 	psdoc->agstates[psdoc->agstate].y = y3;
3339 	ps_printf(psdoc, "%f %f %f %f %f %f curveto\n", x1, y1, x2, y2, x3, y3);
3340 }
3341 /* }}} */
3342 
3343 /* PS_setgray() {{{
3344  * Sets gray value for following line drawing. 0 means black and 1 means white.
3345  */
3346 PSLIB_API void PSLIB_CALL
PS_setgray(PSDoc * psdoc,float gray)3347 PS_setgray(PSDoc *psdoc, float gray) {
3348 	if(NULL == psdoc) {
3349 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3350 		return;
3351 	}
3352 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE)) {
3353 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page' scope."), __FUNCTION__);
3354 		return;
3355 	}
3356 	ps_printf(psdoc, "%f setgray\n", gray);
3357 }
3358 /* }}} */
3359 
3360 /* PS_clip() {{{
3361  * Clips drawing to previously created path.
3362  */
3363 PSLIB_API void PSLIB_CALL
PS_clip(PSDoc * psdoc)3364 PS_clip(PSDoc *psdoc) {
3365 	if(NULL == psdoc) {
3366 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3367 		return;
3368 	}
3369 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH)) {
3370 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path' scope."), __FUNCTION__);
3371 		return;
3372 	}
3373 	ps_printf(psdoc, "clip\n");
3374 	ps_leave_scope(psdoc, PS_SCOPE_PATH);
3375 }
3376 /* }}} */
3377 
3378 /* PS_closepath() {{{
3379  * Closes a path. Connects the last point with the first point.
3380  */
3381 PSLIB_API void PSLIB_CALL
PS_closepath(PSDoc * psdoc)3382 PS_closepath(PSDoc *psdoc) {
3383 	if(NULL == psdoc) {
3384 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3385 		return;
3386 	}
3387 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH)) {
3388 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path' scope."), __FUNCTION__);
3389 		return;
3390 	}
3391 	ps_printf(psdoc, "closepath\n");
3392 }
3393 /* }}} */
3394 
3395 /* PS_closepath_stroke() {{{
3396  * Closes path and draws it.
3397  */
3398 PSLIB_API void PSLIB_CALL
PS_closepath_stroke(PSDoc * psdoc)3399 PS_closepath_stroke(PSDoc *psdoc) {
3400 	if(NULL == psdoc) {
3401 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3402 		return;
3403 	}
3404 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH)) {
3405 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path' scope."), __FUNCTION__);
3406 		return;
3407 	}
3408 	ps_printf(psdoc, "closepath\n");
3409 	ps_setcolor(psdoc, PS_COLORTYPE_STROKE);
3410 	ps_printf(psdoc, "stroke\n");
3411 	ps_leave_scope(psdoc, PS_SCOPE_PATH);
3412 }
3413 /* }}} */
3414 
3415 /* PS_fill_stroke() {{{
3416  * Fills and draws a path
3417  */
3418 PSLIB_API void PSLIB_CALL
PS_fill_stroke(PSDoc * psdoc)3419 PS_fill_stroke(PSDoc *psdoc) {
3420 	if(NULL == psdoc) {
3421 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3422 		return;
3423 	}
3424 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH)) {
3425 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path' scope."), __FUNCTION__);
3426 		return;
3427 	}
3428 	ps_printf(psdoc, "gsave ");
3429 	ps_setcolor(psdoc, PS_COLORTYPE_FILL);
3430 	ps_printf(psdoc, "fill grestore\n");
3431 	ps_setcolor(psdoc, PS_COLORTYPE_STROKE);
3432 	ps_printf(psdoc, "stroke\n");
3433 	ps_leave_scope(psdoc, PS_SCOPE_PATH);
3434 }
3435 /* }}} */
3436 
3437 /* PS_stroke() {{{
3438  * Draws a path
3439  */
3440 PSLIB_API void PSLIB_CALL
PS_stroke(PSDoc * psdoc)3441 PS_stroke(PSDoc *psdoc) {
3442 	if(NULL == psdoc) {
3443 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3444 		return;
3445 	}
3446 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH)) {
3447 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path' scope."), __FUNCTION__);
3448 		return;
3449 	}
3450 	ps_setcolor(psdoc, PS_COLORTYPE_STROKE);
3451 	ps_printf(psdoc, "stroke\n");
3452 	ps_leave_scope(psdoc, PS_SCOPE_PATH);
3453 }
3454 /* }}} */
3455 
3456 /* PS_fill() {{{
3457  * Fills a path.
3458  */
3459 PSLIB_API void PSLIB_CALL
PS_fill(PSDoc * psdoc)3460 PS_fill(PSDoc *psdoc) {
3461 	if(NULL == psdoc) {
3462 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3463 		return;
3464 	}
3465 	if(!ps_check_scope(psdoc, PS_SCOPE_PATH)) {
3466 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'path' scope."), __FUNCTION__);
3467 		return;
3468 	}
3469 	ps_setcolor(psdoc, PS_COLORTYPE_FILL);
3470 	ps_printf(psdoc, "fill\n");
3471 	ps_leave_scope(psdoc, PS_SCOPE_PATH);
3472 }
3473 /* }}} */
3474 
3475 /* PS_shfill() {{{
3476  * Fills a path with a shading.
3477  */
3478 PSLIB_API void PSLIB_CALL
PS_shfill(PSDoc * psdoc,int shading)3479 PS_shfill(PSDoc *psdoc, int shading) {
3480 	PSShading *psshading;
3481 	if(NULL == psdoc) {
3482 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3483 		return;
3484 	}
3485 	psshading = _ps_get_shading(psdoc, shading);
3486 	if(NULL == psshading) {
3487 		ps_error(psdoc, PS_RuntimeError, _("PSShading is null."));
3488 		return;
3489 	}
3490 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3491 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern' or 'template' scope."), __FUNCTION__);
3492 		return;
3493 	}
3494 
3495 	ps_output_shading_dict(psdoc, psshading);
3496 	ps_printf(psdoc, "shfill\n");
3497 }
3498 /* }}} */
3499 
3500 /* PS_save() {{{
3501  * Saves the current context.
3502  */
3503 PSLIB_API void PSLIB_CALL
PS_save(PSDoc * psdoc)3504 PS_save(PSDoc *psdoc) {
3505 	if(NULL == psdoc) {
3506 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3507 		return;
3508 	}
3509 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3510 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3511 		return;
3512 	}
3513 	if(psdoc->agstate >= PS_MAX_GSTATE_LEVELS-1) {
3514 		ps_error(psdoc, PS_Warning, _("No more graphic states available."));
3515 		return;
3516 	}
3517 	/* Take over the old graphics state */
3518 	psdoc->agstate++;
3519 	memcpy(&(psdoc->agstates[psdoc->agstate]), &(psdoc->agstates[psdoc->agstate-1]), sizeof(PSGState));
3520 	psdoc->agstates[psdoc->agstate].x = psdoc->agstates[psdoc->agstate-1].x;
3521 	psdoc->agstates[psdoc->agstate].y = psdoc->agstates[psdoc->agstate-1].y;
3522 
3523 	ps_printf(psdoc, "gsave %% start level %d\n", psdoc->agstate);
3524 }
3525 /* }}} */
3526 
3527 /* PS_restore() {{{
3528  * Restores a previously save context.
3529  */
3530 PSLIB_API void PSLIB_CALL
PS_restore(PSDoc * psdoc)3531 PS_restore(PSDoc *psdoc) {
3532 	if(NULL == psdoc) {
3533 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3534 		return;
3535 	}
3536 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3537 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3538 		return;
3539 	}
3540 	if(psdoc->agstate <= 0) {
3541 		ps_error(psdoc, PS_Warning, _("PS_restore() has been called more often than PS_save()."));
3542 		return;
3543 	}
3544 	ps_printf(psdoc, "grestore %% end level %d\n", psdoc->agstate);
3545 	psdoc->agstate--;
3546 }
3547 /* }}} */
3548 
3549 /* PS_create_gstate() {{{
3550  * Creates a new graphic state and returns its id.
3551  */
3552 PSLIB_API int PSLIB_CALL
PS_create_gstate(PSDoc * psdoc,const char * optlist)3553 PS_create_gstate(PSDoc *psdoc, const char *optlist) {
3554 	ght_hash_table_t *opthash;
3555 	PSGState *psgstate;
3556 	int gstateid;
3557 
3558 	if(NULL == psdoc) {
3559 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3560 		return(0);
3561 	}
3562 	if(NULL != optlist && optlist[0] != '\0') {
3563 		ps_error(psdoc, PS_RuntimeError, _("Option list may not be empty."));
3564 		return(0);
3565 	}
3566 
3567 	if(NULL == (opthash = ps_parse_optlist(psdoc, optlist))) {
3568 		ps_error(psdoc, PS_RuntimeError, _("Error while parsing option list."));
3569 		return(0);
3570 	}
3571 
3572 	if(NULL == (psgstate = (PSGState *) psdoc->malloc(psdoc, sizeof(PSGState), _("Allocate memory for graphic state.")))) {
3573 		ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for graphic state."));
3574 		return(0);
3575 	}
3576 	memset(psgstate, 0, sizeof(PSGState));
3577 	psgstate->opthash = opthash;
3578 	if(0 == (gstateid = _ps_register_gstate(psdoc, psgstate))) {
3579 		ps_error(psdoc, PS_MemoryError, _("Could not register gstate."));
3580 		psdoc->free(psdoc, psgstate);
3581 		return(0);
3582 	}
3583 
3584 	return(gstateid);
3585 }
3586 /* }}} */
3587 
3588 /* PS_set_gstate() {{{
3589  * Sets a graphic state.
3590  */
3591 PSLIB_API void PSLIB_CALL
PS_set_gstate(PSDoc * psdoc,int gstate)3592 PS_set_gstate(PSDoc *psdoc, int gstate) {
3593 	ght_hash_table_t *opthash;
3594 	ght_iterator_t iterator;
3595 	PSGState *psgstate;
3596 	char *optvalue, *optname;
3597 
3598 	if(NULL == psdoc) {
3599 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3600 		return;
3601 	}
3602 	psgstate = _ps_get_gstate(psdoc, gstate);
3603 	if(NULL == psgstate) {
3604 		ps_error(psdoc, PS_RuntimeError, _("PSGState is null."));
3605 		return;
3606 	}
3607 	opthash = psgstate->opthash;
3608 	for(optvalue = ght_first(opthash, &iterator, (void **) &optname); optvalue != NULL; optvalue = ght_next(opthash, &iterator, (void **) &optname)) {
3609 		if(strcmp(optname, "setsmoothness") == 0) {
3610 			float smoothness;
3611 			if(0 == (smoothness = get_optlist_element_as_float(psdoc, opthash, "setsmoothness", &smoothness))) {
3612 				PS_setsmoothness(psdoc, smoothness);
3613 			}
3614 		} else if(strcmp(optname, "linewidth") == 0) {
3615 			float linewidth;
3616 			if(0 == (linewidth = get_optlist_element_as_float(psdoc, opthash, "linewidth", &linewidth))) {
3617 				PS_setlinewidth(psdoc, linewidth);
3618 			}
3619 		} else if(strcmp(optname, "linecap") == 0) {
3620 			int linecap;
3621 			if(0 == (linecap = get_optlist_element_as_int(psdoc, opthash, "linecap", &linecap))) {
3622 				PS_setlinecap(psdoc, linecap);
3623 			}
3624 		} else if(strcmp(optname, "linejoin") == 0) {
3625 			int linejoin;
3626 			if(0 == (linejoin = get_optlist_element_as_int(psdoc, opthash, "linejoin", &linejoin))) {
3627 				PS_setlinejoin(psdoc, linejoin);
3628 			}
3629 		} else if(strcmp(optname, "flatness") == 0) {
3630 			float flatness;
3631 			if(0 == (flatness = get_optlist_element_as_float(psdoc, opthash, "flatness", &flatness))) {
3632 				PS_setflat(psdoc, flatness);
3633 			}
3634 		} else if(strcmp(optname, "miterlimit") == 0) {
3635 			float miterlimit;
3636 			if(0 == (miterlimit = get_optlist_element_as_float(psdoc, opthash, "miterlimit", &miterlimit))) {
3637 				PS_setmiterlimit(psdoc, miterlimit);
3638 			}
3639 		} else if(strcmp(optname, "overprintmode") == 0) {
3640 			int overprintmode;
3641 			if(0 == (overprintmode = get_optlist_element_as_int(psdoc, opthash, "overprintmode", &overprintmode))) {
3642 				PS_setoverprintmode(psdoc, overprintmode);
3643 			}
3644 		} else {
3645 			ps_error(psdoc, PS_Warning, _("Graphic state contains unknown option."));
3646 		}
3647 	}
3648 }
3649 /* }}} */
3650 
3651 /* PS_rotate() {{{
3652  * Rotates the coordinate system, which will effect all following drawing
3653  * operations.
3654  */
3655 PSLIB_API void PSLIB_CALL
PS_rotate(PSDoc * psdoc,float x)3656 PS_rotate(PSDoc *psdoc, float x) {
3657 	if(NULL == psdoc) {
3658 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3659 		return;
3660 	}
3661 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3662 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3663 		return;
3664 	}
3665 	ps_printf(psdoc, "%f rotate\n", x);
3666 }
3667 /* }}} */
3668 
3669 /* PS_translate() {{{
3670  * Translates the coordinate system, which will effect all following drawing
3671  * operations.
3672  */
3673 PSLIB_API void PSLIB_CALL
PS_translate(PSDoc * psdoc,float x,float y)3674 PS_translate(PSDoc *psdoc, float x, float y) {
3675 	if(NULL == psdoc) {
3676 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3677 		return;
3678 	}
3679 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3680 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3681 		return;
3682 	}
3683 	ps_printf(psdoc, "%.2f %.2f translate\n", x, y);
3684 }
3685 /* }}} */
3686 
3687 /* PS_skew() {{{
3688  * FIXME: must be implemented
3689  * Skew the coordinate system
3690  */
3691 PSLIB_API void PSLIB_CALL
PS_skew(PSDoc * psdoc,float alpha,float beta)3692 PS_skew(PSDoc *psdoc, float alpha, float beta) {
3693 	if(NULL == psdoc) {
3694 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3695 		return;
3696 	}
3697 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3698 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3699 		return;
3700 	}
3701 }
3702 /* }}} */
3703 
3704 /* PS_concat() {{{
3705  * FIXME: must be implemented
3706  * Concatenate matrix to current transformation matrix
3707  */
3708 PSLIB_API void PSLIB_CALL
PS_concat(PSDoc * psdoc,float a,float b,float c,float d,float e,float f)3709 PS_concat(PSDoc *psdoc, float a, float b, float c, float d, float e, float f) {
3710 	if(NULL == psdoc) {
3711 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3712 		return;
3713 	}
3714 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3715 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3716 		return;
3717 	}
3718 }
3719 /* }}} */
3720 
3721 /* PS_scale() {{{
3722  * Scales the coordinate system, which will effect all following drawing
3723  * operations.
3724  */
3725 PSLIB_API void PSLIB_CALL
PS_scale(PSDoc * psdoc,float x,float y)3726 PS_scale(PSDoc *psdoc, float x, float y) {
3727 	if(NULL == psdoc) {
3728 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3729 		return;
3730 	}
3731 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3732 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3733 		return;
3734 	}
3735 	ps_printf(psdoc, "%f %f scale\n", x, y);
3736 }
3737 /* }}} */
3738 
3739 /* PS_setcolor() {{{
3740  * Sets drawing color for all following operations
3741  */
3742 PSLIB_API void PSLIB_CALL
PS_setcolor(PSDoc * psdoc,const char * type,const char * colorspace,float c1,float c2,float c3,float c4)3743 PS_setcolor(PSDoc *psdoc, const char *type, const char *colorspace, float c1, float c2, float c3, float c4) {
3744 	int icolorspace = 0;
3745 
3746 	if(NULL == psdoc) {
3747 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3748 		return;
3749 	}
3750 	/* FIXME: Maybe called in pattern only if painttype = 1 */
3751 	if(!ps_check_scope(psdoc, PS_SCOPE_PROLOG|PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3752 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'prolog', 'document', 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3753 		return;
3754 	}
3755 	if(!type || !*type) {
3756 		ps_error(psdoc, PS_RuntimeError, _("Missing parameter type in PS_setcolor()."));
3757 		return;
3758 	}
3759 	if(0 != strcmp(type, "fill") && 0 != strcmp(type, "stroke") && 0 != strcmp(type, "both") && 0 != strcmp(type, "fillstroke")) {
3760 		ps_error(psdoc, PS_RuntimeError, _("Type in PS_setcolor() is none of 'fill', 'stroke' or 'fillstroke'."));
3761 		return;
3762 	}
3763 
3764 	if(!colorspace || !*colorspace) {
3765 		ps_error(psdoc, PS_RuntimeError, _("Missing paramter colorspace in PS_setcolor()."));
3766 		return;
3767 	}
3768 
3769 	if(0 == strcmp(colorspace, "gray")) {
3770 		if(c1 > 1.0 || c1 < 0.0) {
3771 			ps_error(psdoc, PS_RuntimeError, _("Gray value out of range 0-1."));
3772 			return;
3773 		}
3774 		icolorspace = PS_COLORSPACE_GRAY;
3775 //		ps_printf(psdoc, "/DeviceGray setcolorspace %f setcolor\n", c1);
3776 //		ps_printf(psdoc, "%f setgray\n", c1);
3777 	} else if(0 == strcmp(colorspace, "rgb")) {
3778 		if(c1 > 1.0 || c1 < 0.0) {
3779 			ps_error(psdoc, PS_RuntimeError, _("Red value out of range 0-1."));
3780 			return;
3781 		}
3782 		if(c2 > 1.0 || c2 < 0.0) {
3783 			ps_error(psdoc, PS_RuntimeError, _("Green value out of range 0-1."));
3784 			return;
3785 		}
3786 		if(c3 > 1.0 || c3 < 0.0) {
3787 			ps_error(psdoc, PS_RuntimeError, _("Blue value out of range 0-1."));
3788 			return;
3789 		}
3790 		icolorspace = PS_COLORSPACE_RGB;
3791 //		ps_printf(psdoc, "/DeviceRGB setcolorspace %.4f %.4f %.4f setcolor\n", c1, c2, c3);
3792 //		ps_printf(psdoc, "%.4f %.4f %.4f setrgbcolor\n", c1, c2, c3);
3793 	} else if(0 == strcmp(colorspace, "cmyk")) {
3794 		if(c1 > 1.0 || c1 < 0.0) {
3795 			ps_error(psdoc, PS_RuntimeError, _("Cyan value out of range 0-1."));
3796 			return;
3797 		}
3798 		if(c2 > 1.0 || c2 < 0.0) {
3799 			ps_error(psdoc, PS_RuntimeError, _("Magenta value out of range 0-1."));
3800 			return;
3801 		}
3802 		if(c3 > 1.0 || c3 < 0.0) {
3803 			ps_error(psdoc, PS_RuntimeError, _("Yellow value out of range 0-1."));
3804 			return;
3805 		}
3806 		if(c4 > 1.0 || c4 < 0.0) {
3807 			ps_error(psdoc, PS_RuntimeError, _("Black value out of range 0-1."));
3808 			return;
3809 		}
3810 		icolorspace = PS_COLORSPACE_CMYK;
3811 //		ps_printf(psdoc, "/DeviceCMYK setcolorspace %.4f %.4f %.4f %.4f setcolor\n", c1, c2, c3, c4);
3812 //		ps_printf(psdoc, "%.4f %.4f %.4f %.4f setcmykcolor\n", c1, c2, c3, c4);
3813 	} else if(0 == strcmp(colorspace, "pattern")) {
3814 		PSPattern *pspattern = _ps_get_pattern(psdoc, (int) c1);
3815 		if(NULL == pspattern) {
3816 			ps_error(psdoc, PS_RuntimeError, _("PSPattern is null."));
3817 			return;
3818 		}
3819 		icolorspace = PS_COLORSPACE_PATTERN;
3820 //		ps_printf(psdoc, "/Pattern setcolorspace %s setcolor\n", pspattern->name);
3821 //		ps_printf(psdoc, "%s setpattern\n", pspattern->name);
3822 	} else if(0 == strcmp(colorspace, "spot")) {
3823 		PSSpotColor *spotcolor;
3824 		spotcolor = _ps_get_spotcolor(psdoc, (int) c1);
3825 		if(!spotcolor) {
3826 			ps_error(psdoc, PS_RuntimeError, _("Could not find spot color."));
3827 			return;
3828 		}
3829 		if(c2 > 1.0 || c2 < 0.0) {
3830 			ps_error(psdoc, PS_RuntimeError, _("Tint value out of range 0-1."));
3831 			return;
3832 		}
3833 		icolorspace = PS_COLORSPACE_SPOT;
3834 	} else {
3835 		ps_error(psdoc, PS_RuntimeError, _("Colorspace in PS_setcolor() is not in 'gray', 'rgb', 'cmyk', 'spot', or 'pattern'."));
3836 	}
3837 
3838 	if(0 == strcmp(type, "fill") || 0 == strcmp(type, "both") || 0 == strcmp(type, "fillstroke")) {
3839 		psdoc->agstates[psdoc->agstate].fillcolor.prevcolorspace = psdoc->agstates[psdoc->agstate].fillcolor.colorspace;
3840 		psdoc->agstates[psdoc->agstate].fillcolor.colorspace = icolorspace;
3841 		psdoc->agstates[psdoc->agstate].fillcolorinvalid = ps_true;
3842 		switch(icolorspace) {
3843 			case PS_COLORSPACE_GRAY:
3844 				psdoc->agstates[psdoc->agstate].fillcolor.c1 = c1;
3845 				psdoc->agstates[psdoc->agstate].fillcolor.c2 = 0;
3846 				psdoc->agstates[psdoc->agstate].fillcolor.c3 = 0;
3847 				psdoc->agstates[psdoc->agstate].fillcolor.c4 = 0;
3848 				break;
3849 			case PS_COLORSPACE_RGB:
3850 				psdoc->agstates[psdoc->agstate].fillcolor.c1 = c1;
3851 				psdoc->agstates[psdoc->agstate].fillcolor.c2 = c2;
3852 				psdoc->agstates[psdoc->agstate].fillcolor.c3 = c3;
3853 				psdoc->agstates[psdoc->agstate].fillcolor.c4 = 0;
3854 				break;
3855 			case PS_COLORSPACE_CMYK:
3856 				psdoc->agstates[psdoc->agstate].fillcolor.c1 = c1;
3857 				psdoc->agstates[psdoc->agstate].fillcolor.c2 = c2;
3858 				psdoc->agstates[psdoc->agstate].fillcolor.c3 = c3;
3859 				psdoc->agstates[psdoc->agstate].fillcolor.c4 = c4;
3860 				break;
3861 			case PS_COLORSPACE_SPOT:
3862 				psdoc->agstates[psdoc->agstate].fillcolor.c1 = c1;
3863 				psdoc->agstates[psdoc->agstate].fillcolor.c2 = c2;
3864 				break;
3865 			case PS_COLORSPACE_PATTERN:
3866 				psdoc->agstates[psdoc->agstate].fillcolor.pattern = c1;
3867 				break;
3868 		}
3869 	}
3870 	if(0 == strcmp(type, "stroke") || 0 == strcmp(type, "both") || 0 == strcmp(type, "fillstroke")) {
3871 		psdoc->agstates[psdoc->agstate].strokecolor.prevcolorspace = psdoc->agstates[psdoc->agstate].strokecolor.colorspace;
3872 		psdoc->agstates[psdoc->agstate].strokecolor.colorspace = icolorspace;
3873 		psdoc->agstates[psdoc->agstate].strokecolorinvalid = ps_true;
3874 		switch(icolorspace) {
3875 			case PS_COLORSPACE_GRAY:
3876 				psdoc->agstates[psdoc->agstate].strokecolor.c1 = c1;
3877 				psdoc->agstates[psdoc->agstate].strokecolor.c2 = 0;
3878 				psdoc->agstates[psdoc->agstate].strokecolor.c3 = 0;
3879 				psdoc->agstates[psdoc->agstate].strokecolor.c4 = 0;
3880 				break;
3881 			case PS_COLORSPACE_RGB:
3882 				psdoc->agstates[psdoc->agstate].strokecolor.c1 = c1;
3883 				psdoc->agstates[psdoc->agstate].strokecolor.c2 = c2;
3884 				psdoc->agstates[psdoc->agstate].strokecolor.c3 = c3;
3885 				psdoc->agstates[psdoc->agstate].strokecolor.c4 = 0;
3886 				break;
3887 			case PS_COLORSPACE_CMYK:
3888 				psdoc->agstates[psdoc->agstate].strokecolor.c1 = c1;
3889 				psdoc->agstates[psdoc->agstate].strokecolor.c2 = c2;
3890 				psdoc->agstates[psdoc->agstate].strokecolor.c3 = c3;
3891 				psdoc->agstates[psdoc->agstate].strokecolor.c4 = c4;
3892 				break;
3893 			case PS_COLORSPACE_SPOT:
3894 				psdoc->agstates[psdoc->agstate].strokecolor.c1 = c1;
3895 				psdoc->agstates[psdoc->agstate].strokecolor.c2 = c2;
3896 				break;
3897 			case PS_COLORSPACE_PATTERN:
3898 				psdoc->agstates[psdoc->agstate].strokecolor.pattern = c1;
3899 				break;
3900 		}
3901 	}
3902 
3903 	return;
3904 }
3905 /* }}} */
3906 
3907 /* PS_makespotcolor() {{{
3908  * Creates a spot color from the current fill color
3909  */
3910 PSLIB_API int PSLIB_CALL
PS_makespotcolor(PSDoc * psdoc,const char * name,int reserved)3911 PS_makespotcolor(PSDoc *psdoc, const char *name, int reserved) {
3912 	PSSpotColor *spotcolor;
3913 	int ispotcolor;
3914 	int spotcolorid;
3915 
3916 	if(NULL == psdoc) {
3917 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3918 		return(0);
3919 	}
3920 
3921 	if(!ps_check_scope(psdoc, PS_SCOPE_PROLOG|PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3922 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'prolog', 'document', 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3923 		return(0);
3924 	}
3925 
3926 	if(0 != (ispotcolor = _ps_find_spotcolor_by_name(psdoc, name))) {
3927 		return(ispotcolor);
3928 	}
3929 
3930 	if(psdoc->agstates[psdoc->agstate].fillcolor.colorspace != PS_COLORSPACE_GRAY &&
3931 	   psdoc->agstates[psdoc->agstate].fillcolor.colorspace != PS_COLORSPACE_RGB &&
3932 		 psdoc->agstates[psdoc->agstate].fillcolor.colorspace != PS_COLORSPACE_CMYK) {
3933 		ps_error(psdoc, PS_MemoryError, _("Cannot make a spot color from a spot color or pattern."));
3934 		return(0);
3935 	}
3936 
3937 	if(NULL == (spotcolor = (PSSpotColor *) psdoc->malloc(psdoc, sizeof(PSSpotColor), _("Allocate memory for spot color.")))) {
3938 		ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for spot color."));
3939 		return(0);
3940 	}
3941 	memset(spotcolor, 0, sizeof(PSSpotColor));
3942 	if(0 == (spotcolorid = _ps_register_spotcolor(psdoc, spotcolor))) {
3943 		ps_error(psdoc, PS_MemoryError, _("Could not register spotcolor."));
3944 		psdoc->free(psdoc, spotcolor);
3945 		return(0);
3946 	}
3947 
3948 	spotcolor->name = ps_strdup(psdoc, name);
3949 	spotcolor->colorspace = psdoc->agstates[psdoc->agstate].fillcolor.colorspace;
3950 	spotcolor->c1 = psdoc->agstates[psdoc->agstate].fillcolor.c1;
3951 	spotcolor->c2 = psdoc->agstates[psdoc->agstate].fillcolor.c2;
3952 	spotcolor->c3 = psdoc->agstates[psdoc->agstate].fillcolor.c3;
3953 	spotcolor->c4 = psdoc->agstates[psdoc->agstate].fillcolor.c4;
3954 
3955 	return(spotcolorid);
3956 }
3957 /* }}} */
3958 
3959 /* PS_findfont() {{{
3960  * Finds a font. Actually tries to load an afm file for the given fontname.
3961  * Resource of the font must be freed with PS_delete_font()
3962  */
3963 PSLIB_API int PSLIB_CALL
PS_findfont(PSDoc * psdoc,const char * fontname,const char * encoding,int embed)3964 PS_findfont(PSDoc *psdoc, const char *fontname, const char *encoding, int embed) {
3965 	PSFont *psfont;
3966 	ADOBEFONTMETRIC *metrics;
3967 	char *filename;
3968 	const char *myenc;
3969 
3970 	if(NULL == psdoc) {
3971 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
3972 		return(0);
3973 	}
3974 	/* Check if this function does any output. If it does and we are
3975 	 * beyond the header already, then issue
3976 	 * a warning, because if the output goes between pages it will be lost
3977 	 * when extracting certain pages from the document
3978 	 */
3979 	if(psdoc->headerwritten && (embed || (encoding != NULL && encoding[0] != '\0'))) {
3980 		/* If the header has not been written, we do not need to check the
3981 		 * scope.
3982 		 */
3983 		if(ps_check_scope(psdoc, PS_SCOPE_DOCUMENT)) {
3984 			ps_error(psdoc, PS_Warning, _("Calling %s between pages is likely to cause problems when viewing the document. Call in within a page or in the prolog."), __FUNCTION__);
3985 		}
3986 	}
3987 	if(!ps_check_scope(psdoc, PS_SCOPE_PROLOG|PS_SCOPE_PAGE|PS_SCOPE_DOCUMENT|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
3988 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'prolog', 'document', 'page', 'pattern', or 'template' scope."), __FUNCTION__);
3989 		return(0);
3990 	}
3991 
3992 	if(NULL == fontname) {
3993 		ps_error(psdoc, PS_RuntimeError, _("No font name give to PS_findfont()."));
3994 		return(0);
3995 	}
3996 
3997 	if(NULL == (psfont = (PSFont *) psdoc->malloc(psdoc, sizeof(PSFont), _("Allocate memory for font.")))) {
3998 		ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for font."));
3999 		return(0);
4000 	}
4001 
4002 	/* Remember the PSDoc for which the font was loaded. */
4003 	psfont->psdoc = psdoc;
4004 
4005 	filename = ps_find_resource(psdoc, "FontAFM", fontname);
4006 	if(NULL == filename) {
4007 		if(NULL == (filename = (char *) psdoc->malloc(psdoc, strlen(fontname)+5, _("Could not allocate memory for afm filename.")))) {
4008 			return(0);
4009 		}
4010 		strcpy(filename, fontname);
4011 		strcat(filename, ".afm");
4012 		metrics = readadobe(psdoc, filename);
4013 		psdoc->free(psdoc, filename);
4014 	} else {
4015 		metrics = readadobe(psdoc, filename);
4016 	}
4017 
4018 	if(metrics == NULL) {
4019 		ps_error(psdoc, PS_RuntimeError, _("Font metrics could not be loaded (%s)."), fontname);
4020 		psdoc->free(psdoc, psfont);
4021 		return(0);
4022 	}
4023 
4024 	myenc = encoding;
4025 	if((myenc == NULL || myenc[0] == '\0') && (NULL == (myenc = ps_find_resource(psdoc, "FontEncoding", fontname)))) {
4026 		/* The encoding vector for the default encoding is already
4027 		 * part of the prolog and need not be output.
4028 		 */
4029 		readencoding(psdoc, metrics, NULL);
4030 		psfont->encoding = ps_strdup(psdoc, "default");
4031 	} else {
4032 		if(!strcmp(myenc, "builtin")) {
4033 			psfont->encoding = ps_strdup(psdoc, myenc);
4034 			metrics->fontenc = ps_build_enc_from_font(psdoc, metrics);
4035 			if(metrics->codingscheme)
4036 				psdoc->free(psdoc, metrics->codingscheme);
4037 			metrics->codingscheme = ps_strdup(psdoc, metrics->fontname);
4038 
4039 		} else {
4040 			if(0 > readencoding(psdoc, metrics, myenc)) {
4041 				ps_error(psdoc, PS_RuntimeError, _("Encoding file could not be loaded (%s)."), myenc);
4042 				psdoc->free(psdoc, psfont);
4043 				return 0;
4044 			}
4045 			psfont->encoding = ps_strdup(psdoc, myenc);
4046 		}
4047 
4048 		/* If the header is not written, because we are before
4049 		 * the first page, then output the header first.
4050 		 */
4051 		if(psdoc->beginprologwritten == ps_false) {
4052 			ps_write_ps_comments(psdoc);
4053 			ps_write_ps_beginprolog(psdoc);
4054 		}
4055 
4056 		/* Output the encoding vector */
4057 		{
4058 		int i, j;
4059 		ENCODING *fontenc;
4060 		fontenc = ps_build_enc_vector(psdoc, metrics->fontenc);
4061 		ps_printf(psdoc, "/fontenc-%s [\n", metrics->codingscheme);
4062 		for(i=0; i<32; i++) {
4063 			for(j=0; j<8; j++) {
4064 				if((fontenc->vec[i*8+j] != NULL) && (*(fontenc->vec[i*8+j]) != '\0'))
4065 					ps_printf(psdoc, "8#%03o /%s ", i*8+j, fontenc->vec[i*8+j]);
4066 			}
4067 			ps_printf(psdoc, "\n");
4068 		}
4069 		ps_printf(psdoc, "] def\n");
4070 		ps_free_enc_vector(psdoc, fontenc);
4071 		}
4072 	}
4073 
4074 	psfont->metrics = metrics;
4075 
4076 	if(ps_get_bool_parameter(psdoc, "kerning", 0)) {
4077 		filename = ps_find_resource(psdoc, "FontProtusion", fontname);
4078 		if(NULL == filename) {
4079 			if(NULL == (filename = (char *) psdoc->malloc(psdoc, strlen(fontname)+5, _("Could not allocate memory for afm filename.")))) {
4080 				return(0);
4081 			}
4082 			strcpy(filename, fontname);
4083 			strcat(filename, ".pro");
4084 			if(0 > readprotusion(psdoc, psfont, filename))
4085 				ps_error(psdoc, PS_Warning, _("Could not open protusion file: %s"), filename);
4086 			psdoc->free(psdoc, filename);
4087 		} else {
4088 			if(0 > readprotusion(psdoc, psfont, filename))
4089 				ps_error(psdoc, PS_Warning, _("Could not open protusion file: %s"), filename);
4090 		}
4091 	}
4092 
4093 	ps_set_word_spacing(psdoc, psfont, 0.0);
4094 	if(embed) {
4095 		FILE *fp;
4096 		long fsize;
4097 		filename = ps_find_resource(psdoc, "FontOutline", fontname);
4098 
4099 		if(NULL == filename) {
4100 			if(NULL == (filename = (char *) psdoc->malloc(psdoc, strlen(fontname)+5, _("Could not allocate memory for pfb filename.")))) {
4101 				return(0);
4102 			}
4103 			strcpy(filename, fontname);
4104 			strcat(filename, ".pfb");
4105 			fp = ps_open_file_in_path(psdoc, filename);
4106 			psdoc->free(psdoc, filename);
4107 		} else {
4108 			fp = ps_open_file_in_path(psdoc, filename);
4109 		}
4110 		if (fp) {
4111 			unsigned char *bb;
4112 			fseek(fp, 0, SEEK_END);
4113 			fsize = ftell(fp);
4114 			fseek(fp, 0, SEEK_SET);
4115 			if(NULL != (bb = psdoc->malloc(psdoc, fsize, _("Could not allocate memory for content of font outline file.")))) {
4116 				int ts = fread(bb, 1, fsize, fp);
4117 				if ((bb[0] == (unsigned char) 128) && (bb[1]) == (unsigned char) 1) {
4118 					unsigned int ulen, j;
4119 					unsigned int posi,cxxc=0;
4120 					char linebuf[80];
4121 
4122 					/* If the header is not written, because we are before
4123 					 * the first page, then output the header first.
4124 					 */
4125 					if(psdoc->beginprologwritten == ps_false) {
4126 						ps_write_ps_comments(psdoc);
4127 						ps_write_ps_beginprolog(psdoc);
4128 					}
4129 
4130 					ps_printf(psdoc, "%%BeginFont: %s\n", fontname);
4131 					for (posi = 6; posi < fsize; posi++) {
4132 						if ((bb[posi] == (unsigned char) 128) && (bb[posi+1] == (unsigned char) 2))
4133 							break;
4134 						if(bb[posi] != (unsigned char) '\r')
4135 							ps_putc(psdoc, bb[posi]);
4136 						else if(bb[posi] == (unsigned char) '\r' && bb[posi+1] != (unsigned char) '\n')
4137 							ps_putc(psdoc, '\n');
4138 					}
4139 					ulen = bb[posi+2] & 0xff;
4140 					ulen |= (bb[posi+3] << 8) & 0xff00;
4141 					ulen |= (bb[posi+4] << 16) & 0xff0000;
4142 					ulen |= (bb[posi+5] << 24) & 0xff000000;
4143           if (ulen > fsize)
4144 						ulen = fsize-7;
4145 					posi += 6;
4146 					cxxc=0;
4147 					for (j = 0; j < ulen; j++) {
4148 						unsigned char u=bb[posi];
4149 						linebuf[cxxc]=((u >> 4) & 15) + '0';
4150 						if(u>0x9f) linebuf[cxxc]+='a'-':';
4151 						++cxxc;
4152 						u&=15; linebuf[cxxc]=u + '0';
4153 						if(u>0x9) linebuf[cxxc]+='a'-':';
4154 						++posi;
4155 						++cxxc;
4156 						if (cxxc > 72) {
4157 							linebuf[cxxc++]='\n';
4158 							linebuf[cxxc++]=0;
4159 							ps_printf(psdoc, "%s", linebuf);
4160 							cxxc = 0;
4161 						}
4162 					}
4163 					linebuf[cxxc]=0;
4164 					ps_printf(psdoc, "%s\n", linebuf);
4165 					posi += 6;
4166 					for (j = posi; j < fsize; j++) {
4167 						if ((bb[j] == (unsigned char) 128) && (bb[j+1] == (unsigned char) 3))
4168 							break;
4169 						if(bb[j]=='\r')
4170 							ps_printf(psdoc, "\n");
4171 						else
4172 							ps_printf(psdoc, "%c", bb[j]);
4173 					}
4174 					ps_printf(psdoc, "\n");
4175 					cxxc = 0;
4176 					ps_printf(psdoc, "%%EndFont\n");
4177 				} else {
4178 					ps_error(psdoc, PS_Warning, _("Outline of font '%s' does not start with 0x80 0x01"), fontname);
4179 				}
4180 				psdoc->free(psdoc, bb);
4181 			} else {
4182 				ps_error(psdoc, PS_Warning, _("Problems while reading font outline for '%s'."), fontname);
4183 			}
4184 			fclose(fp);
4185 		} else {
4186 			ps_error(psdoc, PS_Warning, _("Font outline could not be loaded for '%s'."), fontname);
4187 		}
4188 	}
4189 	return(_ps_register_font(psdoc, psfont));
4190 }
4191 /* }}} */
4192 
4193 /* PS_load_font() {{{
4194  * Finds a font. Actually tries to load an afm file for the given fontname.
4195  * Resource of the font must be freed with PS_delete_font()
4196  */
4197 PSLIB_API int PSLIB_CALL
PS_load_font(PSDoc * psdoc,const char * fontname,int len,const char * encoding,const char * optlist)4198 PS_load_font(PSDoc *psdoc, const char *fontname, int len, const char *encoding, const char *optlist) {
4199 	ght_hash_table_t *opthash;
4200 	ps_bool optlist_embedding = 0;
4201 	/* Read the option list only, if it is non empty. */
4202 	if(NULL != optlist && optlist[0] != '\0') {
4203 		opthash = ps_parse_optlist(psdoc, optlist);
4204 		if(NULL == opthash) {
4205 			ps_error(psdoc, PS_RuntimeError, _("Error while parsing option list."));
4206 			return(0);
4207 		}
4208 		/* Issue a warning only if a value is given but cannot be parsed */
4209 		if(-2 == get_optlist_element_as_bool(psdoc, opthash, "embedding", &optlist_embedding)) {
4210 			ps_error(psdoc, PS_Warning, _("Value of option list element 'embedding' could not be read, using default."));
4211 		}
4212 		ps_free_optlist(psdoc, opthash);
4213 	}
4214 	return(PS_findfont(psdoc, fontname, encoding, optlist_embedding));
4215 }
4216 /* }}} */
4217 
4218 /* PS_string_geometry() {{{
4219  * Calculates the width of string using the given font.
4220  * If dimension is not NULL it will be filled with the width, descender and
4221  * ascender.
4222  */
4223 PSLIB_API float PSLIB_CALL
PS_string_geometry(PSDoc * psdoc,const char * text,int xlen,int fontid,float size,float * dimension)4224 PS_string_geometry(PSDoc *psdoc, const char *text, int xlen, int fontid, float size, float *dimension) {
4225 	PSFont *psfont;
4226 	float width = 0.0, ascender = 0.0, descender = 0.0, charspacing;
4227 	int i, len;
4228 	int ligonoff;
4229 	int kernonoff;
4230 	char ligdischar;
4231 	ADOBEINFO *prevai=NULL;
4232 
4233 	if(NULL == psdoc) {
4234 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
4235 		return(0.0);
4236 	}
4237 
4238 	if(NULL == text)
4239 		return(0.0);
4240 
4241 	if(0 == fontid)
4242 		psfont = psdoc->font;
4243 	else {
4244 		if(NULL == (psfont = _ps_get_font(psdoc, fontid)))
4245 			return(0.0);
4246 	}
4247 
4248 	if(NULL == psfont) {
4249 		ps_error(psdoc, PS_RuntimeError, _("No font available."));
4250 		return(0.0);
4251 	}
4252 
4253 	if(NULL == psfont->metrics) {
4254 		ps_error(psdoc, PS_RuntimeError, _("No font metrics available. Cannot calculate width of string."));
4255 		return(-1.0);
4256 	}
4257 
4258 	if(0.0 == size)
4259 		size = psfont->size;
4260 
4261 	charspacing = PS_get_value(psdoc, "charspacing", 0) * 1000.0 / size;
4262 	kernonoff = ps_get_bool_parameter(psdoc, "kerning", 1);
4263 	ligonoff = ps_get_bool_parameter(psdoc, "ligatures", 1);
4264 	if(ligonoff) {
4265 		const char *tmp = PS_get_parameter(psdoc, "ligaturedisolvechar", 0.0);
4266 		if(tmp && tmp[0])
4267 			ligdischar = tmp[0];
4268 		else
4269 			ligdischar = '�';
4270 	}
4271 
4272 //	printf("determine width of %s\n", text);
4273 	len = strlen(text);
4274 	if(xlen >= 0)
4275 		len = xlen < len ? xlen : len;
4276 	for(i=0; i<len; i++) {
4277 		ADOBEINFO *ai;
4278 		unsigned char c;
4279 		char *adobename;
4280 
4281 		c = (unsigned char) text[i];
4282 		adobename = ps_inputenc_name(psdoc, c);
4283 		if(adobename && adobename[0] != '\0') {
4284 //			printf("search width for %s in 0x%X\n", adobename, psfont->metrics->adobechars);
4285 			ai = gfindadobe(psfont->metrics->gadobechars, adobename);
4286 			if(ai) {
4287 				if(strcmp(adobename, "space") == 0) {
4288 					width += (float) psfont->wordspace;
4289 				} else {
4290 					char *newadobename;
4291 					int offset = 0;
4292 					if(ligonoff == 1 &&
4293 					   charspacing == 0.0 &&
4294 					   ps_check_for_lig(psdoc, psfont->metrics, ai, &text[i+1], ligdischar, &newadobename, &offset)) {
4295 						if(ps_fontenc_has_glyph(psdoc, psfont->metrics->fontenc, newadobename)) {
4296 							ADOBEINFO *nai = gfindadobe(psfont->metrics->gadobechars, newadobename);
4297 							if(nai) {
4298 								ai = nai;
4299 								i += offset;
4300 							} else {
4301 								ps_error(psdoc, PS_Warning, _("Font '%s' has no ligature '%s', disolving it."), psfont->metrics->fontname, newadobename);
4302 							}
4303 						} else {
4304 							ps_error(psdoc, PS_Warning, _("Font encoding vector of font '%s' has no ligature '%s', disolving it."), psfont->metrics->fontname, newadobename);
4305 						}
4306 					}
4307 					/* At this point either ai is ligature or the current char */
4308 					width += (float) (ai->width);
4309 					if(i < (len-1))
4310 						width += charspacing;
4311 	//			printf("new width is %f\n", width);
4312 					if(kernonoff == 1 && NULL != prevai)
4313 						width += (float) calculatekern(prevai, ai);
4314 					descender = min((float) ai->lly, descender);
4315 					ascender = max((float) ai->ury, ascender);
4316 	//			printf("new width after adding kern is %f\n", width);
4317 				}
4318 			} else {
4319 				ps_error(psdoc, PS_Warning, _("Glyph '%s' not found in metric file."), adobename);
4320 			}
4321 			prevai = ai;
4322 		} else {
4323 			ps_error(psdoc, PS_Warning, _("Character %d not in input encoding vector."), c);
4324 		}
4325 	}
4326 	if(dimension != NULL) {
4327 		dimension[0] = width*size/1000.0;
4328 		dimension[1] = descender*size/1000.0;
4329 		dimension[2] = ascender*size/1000.0;
4330 	}
4331 	return(width*size/1000.0);
4332 }
4333 /* }}} */
4334 
4335 /* PS_stringwidth2() {{{
4336  * Calculates the width of string using the current font.
4337  */
4338 PSLIB_API float PSLIB_CALL
PS_stringwidth2(PSDoc * psdoc,const char * text,int xlen,int fontid,float size)4339 PS_stringwidth2(PSDoc *psdoc, const char *text, int xlen, int fontid, float size) {
4340 	return(PS_string_geometry(psdoc, text, xlen, fontid, size, NULL));
4341 }
4342 /* }}} */
4343 
4344 /* PS_stringwidth() {{{
4345  * Calculates the width of string using the current font.
4346  */
4347 PSLIB_API float PSLIB_CALL
PS_stringwidth(PSDoc * psdoc,const char * text,int fontid,float size)4348 PS_stringwidth(PSDoc *psdoc, const char *text, int fontid, float size) {
4349 	return(PS_string_geometry(psdoc, text, -1, fontid, size, NULL));
4350 }
4351 /* }}} */
4352 
4353 /* PS_deletefont() {{{
4354  * frees the memory allocated by a font
4355  */
4356 PSLIB_API void PSLIB_CALL
PS_deletefont(PSDoc * psdoc,int fontid)4357 PS_deletefont(PSDoc *psdoc, int fontid) {
4358 	_ps_unregister_font(psdoc, fontid);
4359 }
4360 /* }}} */
4361 
4362 /* PS_setfont() {{{
4363  * sets the font for all following text output operations
4364  */
4365 PSLIB_API void PSLIB_CALL
PS_setfont(PSDoc * psdoc,int fontid,float size)4366 PS_setfont(PSDoc *psdoc, int fontid, float size) {
4367 	PSFont *psfont;
4368 	if(NULL == psdoc) {
4369 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
4370 		return;
4371 	}
4372 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_PATTERN|PS_SCOPE_TEMPLATE)) {
4373 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page', 'pattern', or 'template' scope."), __FUNCTION__);
4374 		return;
4375 	}
4376 	psfont = _ps_get_font(psdoc, fontid);
4377 	if(NULL == psfont) {
4378 		ps_error(psdoc, PS_RuntimeError, _("PSFont is null."));
4379 		return;
4380 	}
4381 	psdoc->font = psfont;
4382 	psdoc->font->size = size;
4383 	ps_set_word_spacing(psdoc, psdoc->font, 0.0);
4384 	PS_set_value(psdoc, "leading", size*1.2);
4385 	if(psfont->metrics) {
4386 #ifdef WIN32
4387 		if(_stricmp(psfont->metrics->codingscheme, "FontSpecific") == 0) {
4388 #else
4389 		if(strcasecmp(psfont->metrics->codingscheme, "FontSpecific") == 0) {
4390 #endif
4391 			ps_printf(psdoc, "/%s findfont %f scalefont setfont\n", psfont->metrics->fontname, size);
4392 		} else {
4393 			ps_printf(psdoc, "/%s /%s-%s fontenc-%s ReEncode\n", psfont->metrics->fontname, psfont->metrics->fontname, psfont->metrics->codingscheme, psfont->metrics->codingscheme);
4394 			ps_printf(psdoc, "/%s-%s findfont %f scalefont setfont\n", psfont->metrics->fontname, psfont->metrics->codingscheme, size);
4395 		}
4396 	}
4397 }
4398 /* }}} */
4399 
4400 /* PS_getfont() {{{
4401  * gets the font currently in use
4402  */
4403 PSLIB_API int PSLIB_CALL
4404 PS_getfont(PSDoc *psdoc) {
4405 	if(NULL == psdoc) {
4406 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
4407 		return(0);
4408 	}
4409 
4410 	return(_ps_find_font(psdoc, psdoc->font));
4411 }
4412 /* }}} */
4413 
4414 /* PS_open_image() {{{
4415  * Opens an image which is already in memory
4416  */
4417 PSLIB_API int PSLIB_CALL
4418 PS_open_image(PSDoc *psdoc, const char *type, const char *source, const char *data, long length, int width, int height, int components, int bpc, const char *params) {
4419 	PSImage *psimage;
4420 	int imageid;
4421 	const char *imgreuse;
4422 
4423 	if(NULL == psdoc) {
4424 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
4425 		return(0);
4426 	}
4427 
4428 	imgreuse = PS_get_parameter(psdoc, "imagereuse", 0.0);
4429 	if(!imgreuse || strcmp(imgreuse, "true") == 0) {
4430 		/* If the header is not written, because we are before
4431 		 * the first page, then output the header first.
4432 		 */
4433 		if(psdoc->beginprologwritten == ps_false) {
4434 			ps_write_ps_comments(psdoc);
4435 			ps_write_ps_beginprolog(psdoc);
4436 		}
4437 
4438 		if(ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE)) {
4439 			ps_error(psdoc, PS_Warning, _("Calling %s between pages or on pages for reusable images may cause problems when viewing the document. Call it before the first page."), __FUNCTION__);
4440 		}
4441 
4442 		if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE|PS_SCOPE_PROLOG)) {
4443 			ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' or 'page' scope."), __FUNCTION__);
4444 			return 0;
4445 		}
4446 	} else {
4447 		if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE)) {
4448 			ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' or 'page' scope."), __FUNCTION__);
4449 			return 0;
4450 		}
4451 	}
4452 
4453 	if(NULL == (psimage = (PSImage *) psdoc->malloc(psdoc, sizeof(PSImage), _("Allocate memory for image.")))) {
4454 		ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image."));
4455 		return(0);
4456 	}
4457 	memset(psimage, 0, sizeof(PSImage));
4458 
4459 	if(NULL == (psimage->data = (char *) psdoc->malloc(psdoc, length+1, _("Allocate memory for image data.")))) {
4460 		ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image data."));
4461 		psdoc->free(psdoc, psimage);
4462 		return(0);
4463 	}
4464 
4465 	memcpy(psimage->data, data, length);
4466 	if(!strcmp(type, "eps")) {
4467 		char *bb;
4468 		psimage->data[length] = '\0';
4469 		bb = strstr(psimage->data, "%%BoundingBox:");
4470 		if(bb) {
4471 			float x, y, width, height;
4472 			bb += 15;
4473 			sscanf(bb, "%f %f %f %f", &x, &y, &width, &height);
4474 			psimage->width = (int) width;
4475 			psimage->height = (int) height;
4476 			psimage->length = length;
4477 		}
4478 	} else {
4479 		psimage->length = length;
4480 		psimage->width = width;
4481 		psimage->height = height;
4482 		psimage->components = components;
4483 		psimage->bpc = bpc;
4484 		switch(psimage->components) {
4485 			case 1:
4486 				psimage->colorspace = PS_COLORSPACE_GRAY;
4487 				break;
4488 			case 3:
4489 				psimage->colorspace = PS_COLORSPACE_RGB;
4490 				break;
4491 			case 4:
4492 				psimage->colorspace = PS_COLORSPACE_CMYK;
4493 				break;
4494 			default:
4495 				ps_error(psdoc, PS_RuntimeError, _("Image has unknown number of components per pixel."));
4496 				psdoc->free(psdoc, psimage->data);
4497 				psdoc->free(psdoc, psimage);
4498 				return(0);
4499 		}
4500 	}
4501 
4502 	psimage->type = ps_strdup(psdoc, type);
4503 	if(!imgreuse || strcmp(imgreuse, "true") == 0) {
4504 		char buffer[25];
4505 		psimage->isreusable = 1;
4506 		sprintf(buffer, "Imagedata%d", rand());
4507 		psimage->name = ps_strdup(psdoc, buffer);
4508 	}
4509 
4510 	if(0 == (imageid = _ps_register_image(psdoc, psimage))) {
4511 		ps_error(psdoc, PS_MemoryError, _("Could not register image."));
4512 		psdoc->free(psdoc, psimage->type);
4513 		psdoc->free(psdoc, psimage->data);
4514 		psdoc->free(psdoc, psimage);
4515 		return(0);
4516 	}
4517 
4518 	if(psimage->isreusable) {
4519 		if(!strcmp(type, "eps")) {
4520 			ps_printf(psdoc, "/%s\n", psimage->name);
4521 			ps_printf(psdoc, "currentfile\n");
4522 			ps_printf(psdoc, "<< /Filter /SubFileDecode\n");
4523 			ps_printf(psdoc, "   /DecodeParms << /EODCount 0 /EODString (*EOD*) >>\n");
4524 			ps_printf(psdoc, ">> /ReusableStreamDecode filter\n");
4525 			ps_write(psdoc, psimage->data, psimage->length);
4526 			ps_printf(psdoc, "*EOD*\n");
4527 			ps_printf(psdoc, "def\n");
4528 		} else {
4529 			char *tmpdata;
4530 			const char *imgenc;
4531 			int reallength, j;
4532 
4533 			imgenc = PS_get_parameter(psdoc, "imageencoding", 0.0);
4534 			ps_printf(psdoc, "/%s\n", psimage->name);
4535 			ps_printf(psdoc, "currentfile\n");
4536 			ps_printf(psdoc, "<< /Filter /ASCII%sDecode >>\n", (imgenc == NULL || strcmp(imgenc, "hex") != 0) ? "85" : "Hex");
4537 			ps_printf(psdoc, "/ReusableStreamDecode filter\n");
4538 
4539 			if(psimage->components == 4 && psimage->colorspace == PS_COLORSPACE_RGB) {
4540 				char *dataptr, *tmpdataptr;
4541 				int j;
4542 				dataptr = psimage->data;
4543 				reallength = psimage->height*psimage->width*3;
4544 				tmpdata = tmpdataptr = psdoc->malloc(psdoc, psimage->height*psimage->width*3, _("Allocate memory for temporary image data."));
4545 				for(j=0; j<psimage->length; j++) {
4546 					*tmpdataptr++ = *dataptr++;
4547 					*tmpdataptr++ = *dataptr++;
4548 					*tmpdataptr++ = *dataptr++;
4549 					dataptr++;
4550 				}
4551 			} else {
4552 				tmpdata = psimage->data;
4553 				reallength = psimage->length;
4554 			}
4555 			if(imgenc == NULL || strcmp(imgenc, "hex") != 0)
4556 				ps_ascii85_encode(psdoc, tmpdata, reallength);
4557 			else
4558 				ps_asciihex_encode(psdoc, tmpdata, reallength);
4559 			if(psimage->components == 4 && psimage->colorspace == PS_COLORSPACE_RGB)
4560 				psdoc->free(psdoc, tmpdata);
4561 
4562 			ps_printf(psdoc, "\ndef\n");
4563 		}
4564 	}
4565 
4566 	return(imageid);
4567 }
4568 /* }}} */
4569 
4570 /* PS_open_image_file() {{{
4571  * Opens an image from a file
4572  */
4573 PSLIB_API int PSLIB_CALL
4574 PS_open_image_file(PSDoc *psdoc, const char *type, const char *filename, const char *stringparam, int intparam) {
4575 	PSImage *psimage;
4576 	int imageid;
4577 	const char *imgreuse;
4578 	FILE *fp;
4579 
4580 	if(NULL == psdoc) {
4581 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
4582 		return 0;
4583 	}
4584 
4585 	imgreuse = PS_get_parameter(psdoc, "imagereuse", 0.0);
4586 	if(!imgreuse || strcmp(imgreuse, "true") == 0) {
4587 		/* If the header is not written, because we are before
4588 		 * the first page, then output the header first.
4589 		 */
4590 		if(psdoc->beginprologwritten == ps_false) {
4591 			ps_write_ps_comments(psdoc);
4592 			ps_write_ps_beginprolog(psdoc);
4593 		}
4594 
4595 		if(ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE)) {
4596 			ps_error(psdoc, PS_Warning, _("Calling %s between pages or on pages for reusable images may cause problems when viewing the document. Call it before the first page."), __FUNCTION__);
4597 		}
4598 
4599 		if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE|PS_SCOPE_PROLOG)) {
4600 			ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' or 'page' scope."), __FUNCTION__);
4601 			return 0;
4602 		}
4603 	} else {
4604 		if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE)) {
4605 			ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' or 'page' scope."), __FUNCTION__);
4606 			return 0;
4607 		}
4608 	}
4609 
4610 	if(NULL == filename) {
4611 		ps_error(psdoc, PS_RuntimeError, _("Filename of images is NULL."));
4612 		return 0;
4613 	}
4614 
4615 	if ((fp = fopen(filename, "rb")) == NULL) {
4616 		ps_error(psdoc, PS_RuntimeError, _("Could not open image file %s."), filename);
4617 		return 0;
4618 	}
4619 
4620 	if(NULL == type) {
4621 		ps_error(psdoc, PS_RuntimeError, _("Type of image is NULL."));
4622 		return 0;
4623 	}
4624 
4625 #ifdef HAVE_LIBPNG
4626 	if(0 == strncmp("png", type, 3)) {
4627 #define SIG_READ 8
4628 		char *dataptr, sig[SIG_READ];
4629 		int i;
4630 		int color_type, bit_depth;
4631 		png_structp png_ptr;
4632 		png_infop info_ptr;
4633 		png_uint_32 row_bytes;
4634 		png_bytep *row_pointers;
4635 //		png_bytep row_pointer;
4636 
4637 		png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
4638 //	     png_voidp user_error_ptr, user_error_fn, user_warning_fn);
4639 
4640 		if (png_ptr == NULL) {
4641 			ps_error(psdoc, PS_RuntimeError, _("Could not create png structure."));
4642 			fclose(fp);
4643 			return(0);
4644 		}
4645 
4646 		/* Allocate/initialize the memory for image information.  REQUIRED. */
4647 		info_ptr = png_create_info_struct(png_ptr);
4648 		if (info_ptr == NULL) {
4649 			ps_error(psdoc, PS_RuntimeError, _("Could not create png info structure."));
4650 			fclose(fp);
4651 			png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
4652 			return(0);
4653 		}
4654 
4655 		/* Set error handling if you are using the setjmp/longjmp method (this is
4656 		* the normal method of doing things with libpng).  REQUIRED unless you
4657 		* set up your own error handlers in the png_create_read_struct() earlier.
4658 		*/
4659 		if (setjmp(png_jmpbuf(png_ptr))) {
4660 			/* Free all of the memory associated with the png_ptr and info_ptr */
4661 			ps_error(psdoc, PS_RuntimeError, _("Could not set error handler for libpng."));
4662 			png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
4663 			fclose(fp);
4664 			/* If we get here, we had a problem reading the file */
4665 			return (0);
4666 		}
4667 
4668 		if (fread(sig, 1, SIG_READ, fp) == 0 || !png_check_sig(sig, SIG_READ)) {
4669 			fclose(fp);
4670 			ps_error(psdoc, PS_RuntimeError, "File '%s' is not a PNG file", filename);
4671 			return (0);
4672 		}
4673 
4674 		/* Set up the input control if you are using standard C streams */
4675 		png_init_io(png_ptr, fp);
4676 
4677 		/* If we have already read some of the signature */
4678 		png_set_sig_bytes(png_ptr, SIG_READ);
4679 
4680 		/*
4681 		* If you have enough memory to read in the entire image at once,
4682 		* and you need to specify only transforms that can be controlled
4683 		* with one of the PNG_TRANSFORM_* bits (this presently excludes
4684 		* dithering, filling, setting background, and doing gamma
4685 		* adjustment), then you can read the entire image (including
4686 		* pixels) into the info structure with this call:
4687 		*/
4688 #define mymemory
4689 #ifdef mymemory
4690 		png_read_info(png_ptr, info_ptr);
4691 		color_type = png_get_color_type(png_ptr, info_ptr);
4692 		bit_depth = png_get_bit_depth(png_ptr, info_ptr);
4693 		if (bit_depth == 16) {
4694 			png_set_strip_16(png_ptr);
4695 		}
4696 
4697 		/* FIXME: Quick fix to circumvent a segm fault in ps_place_image()
4698 		 * when rgb images with alpha channel are loaded.
4699 		 */
4700 		if (color_type & PNG_COLOR_MASK_ALPHA)
4701 			png_set_strip_alpha(png_ptr);
4702 
4703 		/* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
4704 		if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
4705 #if defined(PNG_1_0_X) || defined (PNG_1_2_X)
4706 			png_set_gray_1_2_4_to_8(png_ptr);
4707 #else
4708 			png_set_expand_gray_1_2_4_to_8(png_ptr);
4709 #endif
4710 
4711 		png_read_update_info(png_ptr, info_ptr);
4712 #else
4713 		/* Read the entire image */
4714 		png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, png_voidp_NULL);
4715 #endif
4716 
4717 		if(NULL == (psimage = (PSImage *) psdoc->malloc(psdoc, sizeof(PSImage), _("Allocate memory for image.")))) {
4718 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image."));
4719 			fclose(fp);
4720 			return(0);
4721 		}
4722 		memset(psimage, 0, sizeof(PSImage));
4723 
4724 		if(stringparam != NULL && strcmp(stringparam, "mask") == 0) {
4725 			psimage->ismask = ps_true;
4726 		}
4727 
4728 		if(stringparam != NULL && strcmp(stringparam, "masked") == 0) {
4729 			PSImage *psmaskimage = _ps_get_image(psdoc, intparam);
4730 			if(!psmaskimage) {
4731 				ps_error(psdoc, PS_RuntimeError, _("Could not find image mask."));
4732 				psdoc->free(psdoc, psimage);
4733 
4734 				return(0);
4735 			}
4736 			psimage->imagemask = psmaskimage;
4737 		}
4738 		psimage->psdoc = psdoc;
4739 		psimage->type = ps_strdup(psdoc, type);
4740 		psimage->width = png_get_image_width(png_ptr, info_ptr);
4741 		psimage->height = png_get_image_height(png_ptr, info_ptr);
4742 		psimage->components = png_get_channels(png_ptr, info_ptr);
4743 		psimage->bpc = png_get_bit_depth(png_ptr, info_ptr);
4744 		switch(png_get_color_type(png_ptr, info_ptr)) {
4745 			case PNG_COLOR_TYPE_GRAY:
4746 			case PNG_COLOR_TYPE_GRAY_ALPHA:
4747 				psimage->colorspace = PS_COLORSPACE_GRAY;
4748 				break;
4749 			case PNG_COLOR_TYPE_RGB:
4750 			case PNG_COLOR_TYPE_RGB_ALPHA:
4751 				psimage->colorspace = PS_COLORSPACE_RGB;
4752 				break;
4753 			case PNG_COLOR_TYPE_PALETTE:
4754 				psimage->colorspace = PS_COLORSPACE_INDEXED;
4755 				break;
4756 		}
4757 		if(psimage->colorspace == PS_COLORSPACE_INDEXED) {
4758 			int num_palette;
4759 			png_colorp palette;
4760 			png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
4761 			if(NULL == (psimage->palette = psdoc->malloc(psdoc, sizeof(PSColor) * num_palette, _("Allocate memory for color palette.")))) {
4762 				ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for color palette."));
4763 				psdoc->free(psdoc, psimage);
4764 				fclose(fp);
4765 				return(0);
4766 			}
4767 			for(i=0; i<num_palette; i++) {
4768 				psimage->palette[i].colorspace = PS_COLORSPACE_RGB;
4769 				psimage->palette[i].c1 = palette[i].red/255.0;
4770 				psimage->palette[i].c2 = palette[i].green/255.0;
4771 				psimage->palette[i].c3 = palette[i].blue/255.0;
4772 				psimage->palette[i].c4 = 0.0;
4773 			}
4774 			psimage->numcolors = num_palette;
4775 		}
4776 
4777 		row_bytes = png_get_rowbytes(png_ptr, info_ptr);
4778 		psimage->length = row_bytes * psimage->height;
4779 /*
4780 		fprintf(stderr, "%dx%d pixel\n", psimage->width, psimage->height);
4781 		fprintf(stderr, "%d channels\n", psimage->components);
4782 		fprintf(stderr, "%d bits per color\n", psimage->bpc);
4783 		fprintf(stderr, "%d bytes per row\n", row_bytes);
4784 */
4785 
4786 		if(NULL == (psimage->data = psdoc->malloc(psdoc, psimage->height*row_bytes, _("Allocate memory for image data.")))) {
4787 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image data."));
4788 			psdoc->free(psdoc, psimage);
4789 			fclose(fp);
4790 			return(0);
4791 		}
4792 
4793 #ifdef mymemory
4794 		if(NULL == (row_pointers = psdoc->malloc(psdoc, psimage->height*sizeof(png_bytep), _("Allocate memory for row pointers of png image.")))) {
4795 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for row pointer of png image."));
4796 			psdoc->free(psdoc, psimage->data);
4797 			psdoc->free(psdoc, psimage);
4798 			fclose(fp);
4799 			return(0);
4800 		}
4801 
4802 		dataptr = psimage->data;
4803 		for(i=0; i<psimage->height; i++) {
4804 			row_pointers[i] = dataptr;
4805 //			fprintf(stderr, "Reading row %d\n", i);
4806 //			png_read_row(png_ptr, dataptr, NULL);
4807 			dataptr += row_bytes;
4808 		}
4809 		png_read_image(png_ptr, row_pointers);
4810 		png_read_end(png_ptr, NULL);
4811 		psdoc->free(psdoc, row_pointers);
4812 #else
4813 		row_pointers = png_get_rows(png_ptr, info_ptr);
4814 		if(row_pointers == NULL) {
4815 			ps_error(psdoc, PS_MemoryError, _("Could get array of image rows."));
4816 			psdoc->free(psdoc, psimage->data);
4817 			return(0);
4818 		}
4819 		dataptr = psimage->data;
4820 		for(i=0; i<psimage->height; i++) {
4821 			fprintf(stderr, "Copying %d row 0x%X -> 0x%X\n", i, row_pointers[i], dataptr);
4822 			memcpy(dataptr, row_pointers[i], row_bytes);
4823 			dataptr += row_bytes;
4824 		}
4825 #endif
4826 		/* clean up after the read, and free any memory allocated - REQUIRED */
4827 		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
4828 
4829 	} else
4830 #endif /* HAVE_LIBPNG */
4831 #ifdef HAVE_LIBJPEG
4832 	if(0 == strncmp("jpeg", type, 4)) {
4833 		char *dataptr;
4834 		struct jpeg_decompress_struct cinfo;
4835 		JSAMPARRAY buffer;
4836 		int row_stride;
4837 		struct jpeg_error_mgr jerr;
4838 
4839 		cinfo.err = jpeg_std_error(&jerr);
4840 		jpeg_create_decompress(&cinfo);
4841 		jpeg_stdio_src(&cinfo, fp);
4842 		jpeg_read_header(&cinfo, TRUE);
4843 		jpeg_start_decompress(&cinfo);
4844 		row_stride = cinfo.output_width * cinfo.output_components;
4845 
4846 		if(NULL == (psimage = (PSImage *) psdoc->malloc(psdoc, sizeof(PSImage), _("Allocate memory for image.")))) {
4847 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image."));
4848 			fclose(fp);
4849 			return(0);
4850 		}
4851 		memset(psimage, 0, sizeof(PSImage));
4852 
4853 		psimage->psdoc = psdoc;
4854 		psimage->type = ps_strdup(psdoc, type);
4855 		psimage->width = cinfo.output_width;
4856 		psimage->height = cinfo.output_height;
4857 		psimage->components = cinfo.output_components;
4858 		psimage->bpc = 8;
4859 		switch(cinfo.out_color_space) {
4860 			case JCS_GRAYSCALE:
4861 				psimage->colorspace = PS_COLORSPACE_GRAY;
4862 				break;
4863 			case JCS_RGB:
4864 				psimage->colorspace = PS_COLORSPACE_RGB;
4865 				break;
4866 			case JCS_CMYK:
4867 				psimage->colorspace = PS_COLORSPACE_CMYK;
4868 				break;
4869 		}
4870 
4871 //		if(psimage->components == 1)
4872 //			psimage->colorspace = PS_COLORSPACE_GRAY;
4873 //		else
4874 //			psimage->colorspace = PS_COLORSPACE_RGB;
4875 		psimage->length = psimage->width * psimage->height * psimage->components;
4876 		if(NULL == (psimage->data = psdoc->malloc(psdoc, psimage->length, _("Allocate memory for image data.")))) {
4877 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image data."));
4878 			fclose(fp);
4879 			return(0);
4880 		}
4881 
4882 		buffer = (*cinfo.mem->alloc_sarray)
4883 			    ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
4884 		dataptr = psimage->data;
4885 		while (cinfo.output_scanline < cinfo.output_height) {
4886 			jpeg_read_scanlines(&cinfo, buffer, 1);
4887 			memcpy(dataptr, buffer[0], row_stride);
4888 			dataptr += row_stride;
4889 		}
4890 		jpeg_finish_decompress(&cinfo);
4891 		jpeg_destroy_decompress(&cinfo);
4892 	} else
4893 #endif /* HAVE_LIBJPEG */
4894 #ifdef HAVE_LIBGIF
4895 	if(0 == strncmp("gif", type, 3)) {
4896 		GifFileType* gft;
4897 		int i, numcolors = 0, alphapalette = -1;
4898 		ColorMapObject* colormap;
4899 		GifColorType c;
4900 		unsigned char *dataptr;
4901 #if GIFLIB_MAJOR > 5 || GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1
4902 		int error;
4903 		  #define GIF_OPEN_FILENAME(filename) DGifOpenFileName(filename, &error)
4904 		  #define GIF_CLOSE_FILE(gif) DGifCloseFile(gif, &error)
4905 #else
4906 		  #define GIF_OPEN_FILENAME(filename) DGifOpenFileName(filename)
4907 		  #define GIF_CLOSE_FILE(gif) DGifCloseFile(gif)
4908 #endif
4909 
4910 		if(NULL == (psimage = (PSImage *) psdoc->malloc(psdoc, sizeof(PSImage), _("Allocate memory for image.")))) {
4911 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image."));
4912 			fclose(fp);
4913 			return(0);
4914 		}
4915 		memset(psimage, 0, sizeof(PSImage));
4916 
4917 		if ((gft = GIF_OPEN_FILENAME(filename)) == NULL) {
4918 			ps_error(psdoc, PS_RuntimeError, _("%s is not a gif file!"), filename);
4919 			fclose(fp);
4920 			return(0);
4921 		}
4922 		if (DGifSlurp(gft) != GIF_OK) {
4923 #if GIFLIB_MAJOR > 5 || GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1
4924 			ps_error(psdoc, PS_RuntimeError, _("Error %d while reading gif file!"), error);
4925 #else
4926 			ps_error(psdoc, PS_RuntimeError, _("Error %d while reading gif file!"), GifLastError());
4927 #endif
4928 			fclose(fp);
4929 			return(0);
4930     }
4931 
4932 		psimage->psdoc = psdoc;
4933 		psimage->type = ps_strdup(psdoc, type);
4934 		psimage->width = gft->SWidth;
4935 		psimage->height = gft->SHeight;
4936 		psimage->components = 1;
4937 		psimage->bpc = 8;
4938 		psimage->colorspace = PS_COLORSPACE_INDEXED;
4939 
4940 		// look for the transparent color extension
4941 		for (i = 0; i < gft->SavedImages[0].ExtensionBlockCount; ++i) {
4942 			ExtensionBlock* eb = gft->SavedImages[0].ExtensionBlocks + i;
4943 			if (eb->Function == GRAPHICS_EXT_FUNC_CODE && eb->ByteCount == 4) {
4944 				int has_transparency = ((eb->Bytes[0] & 1) == 1);
4945 				if (has_transparency)
4946 					alphapalette = eb->Bytes[3];
4947 			}
4948 		}
4949 		colormap = gft->Image.ColorMap ? gft->Image.ColorMap : gft->SColorMap;
4950 		numcolors = colormap->ColorCount;
4951 
4952 		if(NULL == (psimage->palette = psdoc->malloc(psdoc, sizeof(PSColor) * numcolors, _("Allocate memory for color palette.")))) {
4953 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for color palette."));
4954 			GIF_CLOSE_FILE(gft);
4955 			psdoc->free(psdoc, psimage);
4956 			fclose(fp);
4957 			return(0);
4958 		}
4959 
4960 		for (i=0; i<numcolors; i++) {
4961 			psimage->palette[i].colorspace = PS_COLORSPACE_RGB;
4962 			c = colormap->Colors[i];
4963 			if (i == alphapalette)
4964 				psimage->palette[i].c1 = psimage->palette[i].c2 = psimage->palette[i].c3 = psimage->palette[i].c4 = 1;// Fully transparent
4965 			else {
4966 				psimage->palette[i].c1 = c.Red/255.0;
4967 				psimage->palette[i].c2 = c.Green/255.0;
4968 				psimage->palette[i].c3 = c.Blue/255.0;
4969 				psimage->palette[i].c4 = 0;
4970 			}
4971 		}
4972 		psimage->numcolors = numcolors;
4973 
4974 		dataptr = (unsigned char *) gft->SavedImages[0].RasterBits;
4975 
4976 		psimage->length = psimage->width * psimage->height;
4977 		if(NULL == (psimage->data = psdoc->malloc(psdoc, psimage->length, _("Allocate memory for image data.")))) {
4978 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image data."));
4979 			GIF_CLOSE_FILE(gft);
4980 			psdoc->free(psdoc, psimage->palette);
4981 			psdoc->free(psdoc, psimage);
4982 			fclose(fp);
4983 			return(0);
4984 		}
4985 
4986 		if(gft->Image.Interlace) {
4987 			unsigned char *out;
4988 			unsigned int row;
4989 			for (row = 0; row < psimage->height; row += 8) {
4990 				out = psimage->data + row*psimage->width;
4991 				memcpy(out, dataptr, psimage->width);
4992 				dataptr += psimage->width;
4993 			}
4994 			for (row = 4; row < psimage->height; row += 8) {
4995 				out = psimage->data + row*psimage->width;
4996 				memcpy(out, dataptr, psimage->width);
4997 				dataptr += psimage->width;
4998 			}
4999 			for (row = 2; row < psimage->height; row += 4) {
5000 				out = psimage->data + row*psimage->width;
5001 				memcpy(out, dataptr, psimage->width);
5002 				dataptr += psimage->width;
5003 			}
5004 			for (row = 1; row < psimage->height; row += 2) {
5005 				out = psimage->data + row*psimage->width;
5006 				memcpy(out, dataptr, psimage->width);
5007 				dataptr += psimage->width;
5008 			}
5009 		} else {
5010 			memcpy(psimage->data, dataptr, psimage->length);
5011 		}
5012 
5013 		GIF_CLOSE_FILE(gft);
5014 
5015 	} else
5016 #endif /* HAVE_LIBGIF */
5017 #ifdef HAVE_LIBTIFF
5018 	if(0 == strncmp("tiff", type, 4)) {
5019 		TIFF* tif;
5020 		uint32* raster;
5021 
5022 		if(NULL == (psimage = (PSImage *) psdoc->malloc(psdoc, sizeof(PSImage), _("Allocate memory for image.")))) {
5023 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image."));
5024 			fclose(fp);
5025 			return(0);
5026 		}
5027 		memset(psimage, 0, sizeof(PSImage));
5028 
5029 		psimage->psdoc = psdoc;
5030 		psimage->type = ps_strdup(psdoc, type);
5031 		psimage->components = 3;
5032 		psimage->bpc = 8;
5033 		psimage->colorspace = PS_COLORSPACE_RGB;
5034 
5035 		if(NULL == (tif = TIFFOpen(filename, "r"))) {
5036 			psdoc->free(psdoc, psimage);
5037 			fclose(fp);
5038 			return(0);
5039 		}
5040 
5041 		TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &(psimage->width));
5042 		TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &(psimage->height));
5043 		psimage->length = psimage->width * psimage->height * 3;
5044 
5045 		raster = (uint32*) _TIFFmalloc(psimage->width * psimage->height * sizeof (uint32));
5046 		if (raster != NULL) {
5047 			if (TIFFReadRGBAImageOriented(tif, psimage->width, psimage->height, raster, ORIENTATION_TOPLEFT, 0)) {
5048 				unsigned char *src, *dst;
5049 				int pixel_count = psimage->width * psimage->height;
5050 
5051 				if(NULL == (psimage->data = psdoc->malloc(psdoc, psimage->length, _("Allocate memory for image data.")))) {
5052 					ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image data."));
5053 					psdoc->free(psdoc, psimage);
5054 					_TIFFfree(raster);
5055 					TIFFClose(tif);
5056 					fclose(fp);
5057 					return(0);
5058 				}
5059 
5060 				dst = psimage->data;
5061 				src = (unsigned char *) raster;
5062 				while( pixel_count > 0 ) {
5063 					/* The raster is in ABGR order */
5064 					dst[0] = src[3];
5065 					dst[1] = src[2];
5066 					dst[2] = src[1];
5067 					src += 4;
5068 					dst += 3;
5069 					pixel_count--;
5070 				}
5071 			}
5072 			_TIFFfree(raster);
5073 		}
5074 		TIFFClose(tif);
5075 	} else
5076 #endif /* HAVE_LIBTIFF */
5077 #ifndef DISABLE_BMP
5078 	if(0 == strncmp("bmp", type, 3)) {
5079 		int w, h, bps, spp, xres, yres, i;
5080 		unsigned char *color_table, *ct;
5081 		int color_table_size, color_table_elements;
5082 		unsigned char *data;
5083 
5084 		if(NULL == (psimage = (PSImage *) psdoc->malloc(psdoc, sizeof(PSImage), _("Allocate memory for image.")))) {
5085 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image."));
5086 			fclose(fp);
5087 			return(0);
5088 		}
5089 		memset(psimage, 0, sizeof(PSImage));
5090 
5091 		psimage->psdoc = psdoc;
5092 		psimage->type = ps_strdup(psdoc, type);
5093 
5094 		if(NULL == (data = read_bmp(psdoc, filename, &w, &h, &bps, &spp, &xres, &yres,
5095 						 &color_table, &color_table_size, &color_table_elements))) {
5096 			ps_error(psdoc, PS_MemoryError, _("Could not read bmp file."));
5097 			psdoc->free(psdoc, psimage);
5098 			fclose(fp);
5099 			return(0);
5100 		}
5101 		psimage->width = w;
5102 		psimage->height = h;
5103 		if(NULL != color_table) {
5104 			psimage->components = 1;
5105 			psimage->bpc = bps;
5106 			psimage->colorspace = PS_COLORSPACE_INDEXED;
5107 			psimage->length = w * h;
5108 
5109 			if(NULL == (psimage->palette = psdoc->malloc(psdoc, sizeof(PSColor) * color_table_size, _("Allocate memory for color palette.")))) {
5110 				ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for color palette."));
5111 				psdoc->free(psdoc, psimage);
5112 				fclose(fp);
5113 				return(0);
5114 			}
5115 
5116 			ct = color_table;
5117 			for (i=0; i<color_table_size; i++) {
5118 				psimage->palette[i].colorspace = PS_COLORSPACE_RGB;
5119 				psimage->palette[i].c3 = *ct++/255.0;
5120 				psimage->palette[i].c2 = *ct++/255.0;
5121 				psimage->palette[i].c1 = *ct++/255.0;
5122 				psimage->palette[i].c4 = *ct++/255.0;
5123 			}
5124 			psimage->numcolors = color_table_size;
5125 			psdoc->free(psdoc, color_table);
5126 		} else {
5127 			psimage->components = spp;
5128 			psimage->bpc = bps;
5129 			psimage->colorspace = PS_COLORSPACE_RGB;
5130 			psimage->length = w * h * 3;
5131 		}
5132 		/* data has been allocated in read_bmp() */
5133 		psimage->data = data;
5134 	} else
5135 #endif
5136 	if(0 == strncmp("eps", type, 3)) {
5137 		char *bb;
5138 		struct stat statbuf;
5139 		if(0 > stat(filename, &statbuf)) {
5140 			ps_error(psdoc, PS_RuntimeError, _("Could not stat eps file."));
5141 			fclose(fp);
5142 			return(0);
5143 		}
5144 		if(NULL == (psimage = (PSImage *) psdoc->malloc(psdoc, sizeof(PSImage), _("Allocate memory for image.")))) {
5145 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image."));
5146 			fclose(fp);
5147 			return(0);
5148 		}
5149 		memset(psimage, 0, sizeof(PSImage));
5150 		psimage->type = ps_strdup(psdoc, type);
5151 
5152 		if(NULL == (psimage->data = psdoc->malloc(psdoc, statbuf.st_size+1, _("Allocate memory for image data.")))) {
5153 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for image data."));
5154 			psdoc->free(psdoc, psimage);
5155 			fclose(fp);
5156 			return(0);
5157 		}
5158 		psimage->psdoc = psdoc;
5159 		psimage->length = statbuf.st_size;
5160 
5161 		fread(psimage->data, statbuf.st_size, 1, fp);
5162 		psimage->data[statbuf.st_size] = '\0';
5163 		bb = strstr(psimage->data, "%%BoundingBox:");
5164 		if(bb) {
5165 			float x, y, width, height;
5166 			bb += 15;
5167 			sscanf(bb, "%f %f %f %f", &x, &y, &width, &height);
5168 			psimage->width = (int) width;
5169 			psimage->height = (int) height;
5170 		}
5171 	} else {
5172 		ps_error(psdoc, PS_RuntimeError, _("Images of type '%s' not supported."), type);
5173 		fclose(fp);
5174 		return(0);
5175 	}
5176 	/* close the file */
5177 	fclose(fp);
5178 
5179 	/* that's it */
5180 
5181 	if(!imgreuse || strcmp(imgreuse, "true") == 0) {
5182 		char buffer[25];
5183 		psimage->isreusable = 1;
5184 		sprintf(buffer, "Imagedata%d", rand());
5185 		psimage->name = ps_strdup(psdoc, buffer);
5186 	}
5187 
5188 	if(0 == (imageid = _ps_register_image(psdoc, psimage))) {
5189 		ps_error(psdoc, PS_MemoryError, _("Could not register image."));
5190 		psdoc->free(psdoc, psimage->type);
5191 		psdoc->free(psdoc, psimage->data);
5192 		psdoc->free(psdoc, psimage);
5193 		return(0);
5194 	}
5195 
5196 	if(psimage->isreusable) {
5197 		if(!strcmp(type, "eps")) {
5198 			ps_printf(psdoc, "/%s\n", psimage->name);
5199 			ps_printf(psdoc, "currentfile\n");
5200 			ps_printf(psdoc, "<< /Filter /SubFileDecode\n");
5201 			ps_printf(psdoc, "   /DecodeParms << /EODCount 0 /EODString (*EOD*) >>\n");
5202 			ps_printf(psdoc, ">> /ReusableStreamDecode filter\n");
5203 			ps_write(psdoc, psimage->data, psimage->length);
5204 			ps_printf(psdoc, "*EOD*\n");
5205 			ps_printf(psdoc, "def\n");
5206 		} else {
5207 			char *tmpdata;
5208 			const char *imgenc;
5209 			int reallength, j;
5210 
5211 			imgenc = PS_get_parameter(psdoc, "imageencoding", 0.0);
5212 			ps_printf(psdoc, "/%s\n", psimage->name);
5213 			ps_printf(psdoc, "currentfile\n");
5214 			ps_printf(psdoc, "<< /Filter /ASCII%sDecode >>\n", (imgenc == NULL || strcmp(imgenc, "hex") != 0) ? "85" : "Hex");
5215 			ps_printf(psdoc, "/ReusableStreamDecode filter\n");
5216 
5217 			if(psimage->components == 4 && psimage->colorspace == PS_COLORSPACE_RGB) {
5218 				char *dataptr, *tmpdataptr;
5219 				int j;
5220 				dataptr = psimage->data;
5221 				reallength = psimage->height*psimage->width*3;
5222 				tmpdata = tmpdataptr = psdoc->malloc(psdoc, psimage->height*psimage->width*3, _("Allocate memory for temporary image data."));
5223 				for(j=0; j<psimage->length; j++) {
5224 					*tmpdataptr++ = *dataptr++;
5225 					*tmpdataptr++ = *dataptr++;
5226 					*tmpdataptr++ = *dataptr++;
5227 					dataptr++;
5228 				}
5229 			} else {
5230 				tmpdata = psimage->data;
5231 				reallength = psimage->length;
5232 			}
5233 			if(imgenc == NULL || strcmp(imgenc, "hex") != 0)
5234 				ps_ascii85_encode(psdoc, tmpdata, reallength);
5235 			else
5236 				ps_asciihex_encode(psdoc, tmpdata, reallength);
5237 			if(psimage->components == 4 && psimage->colorspace == PS_COLORSPACE_RGB)
5238 				psdoc->free(psdoc, tmpdata);
5239 
5240 			ps_printf(psdoc, "\ndef\n");
5241 		}
5242 	}
5243 	return(imageid);
5244 }
5245 /* }}} */
5246 
5247 /* PS_place_image() {{{
5248  * Place an image on the page
5249  */
5250 PSLIB_API void PSLIB_CALL
5251 PS_place_image(PSDoc *psdoc, int imageid, float x, float y, float scale) {
5252 	PSImage *image;
5253 	const char *imgreuse;
5254 
5255 	if(NULL == psdoc) {
5256 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
5257 		return;
5258 	}
5259 	imgreuse = PS_get_parameter(psdoc, "imagereuse", 0.0);
5260 	if(!imgreuse || strcmp(imgreuse, "true") == 0) {
5261 		if(!ps_check_scope(psdoc, PS_SCOPE_PAGE|PS_SCOPE_TEMPLATE)) {
5262 			ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page' or 'template' scope."), __FUNCTION__);
5263 			return;
5264 		}
5265 	} else {
5266 		if(!ps_check_scope(psdoc, PS_SCOPE_PAGE)) {
5267 			ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page' scope."), __FUNCTION__);
5268 			return;
5269 		}
5270 	}
5271 
5272 	if(scale == 0.0) {
5273 		ps_error(psdoc, PS_Warning, _("Scaling image to 0.0 will make it disappear."), __FUNCTION__);
5274 	}
5275 
5276 	image = _ps_get_image(psdoc, imageid);
5277 	if(NULL == image) {
5278 		ps_error(psdoc, PS_RuntimeError, _("PSImage is null."));
5279 		return;
5280 	}
5281 	if(NULL == image->type) {
5282 		ps_error(psdoc, PS_RuntimeError, _("Image has no type set."));
5283 		return;
5284 	}
5285 
5286 	/* All images must have data, except for templates */
5287 	if(NULL == image->data && strcmp(image->type, "template")) {
5288 		ps_error(psdoc, PS_RuntimeError, _("Image has no data."));
5289 		return;
5290 	}
5291 
5292 	if(
5293 #ifdef HAVE_LIBPNG
5294 	  0 == strncmp(image->type, "png", 3) ||
5295 #endif
5296 #ifdef HAVE_LIBJPEG
5297 		0 == strncmp(image->type, "jpeg", 4) ||
5298 #endif
5299 #ifdef HAVE_LIBGIF
5300 		0 == strncmp(image->type, "gif", 3) ||
5301 #endif
5302 #ifdef HAVE_LIBTIFF
5303 		0 == strncmp(image->type, "tiff", 4) ||
5304 #endif
5305 #ifndef DISABLE_BMP
5306 		0 == strncmp(image->type, "bmp", 3) ||
5307 #endif
5308 		0 == strncmp(image->type, "memory", 6) ) {
5309 		unsigned char *dataptr, *tmpdata, *tmpdataptr;
5310 		const char *imgenc;
5311 		int i, j, k, reallength;
5312 		ps_printf(psdoc, "gsave\n");
5313 #ifdef LEVEL1
5314 		ps_printf(psdoc, "%.2f %.2f scale\n", scale*image->width, scale*image->height);
5315 		ps_printf(psdoc, "/readstring { currentfile exch readhexstring pop } bind def\n");
5316 		ps_printf(psdoc, "/rpicstr %d string def\n", image->width);
5317 		ps_printf(psdoc, "/gpicstr %d string def\n", image->width);
5318 		ps_printf(psdoc, "/bpicstr %d string def\n", image->width);
5319 		ps_printf(psdoc, "%d %d %d\n", image->width, image->height, image->bpc);
5320 		ps_printf(psdoc, "[%d 0 0 %d %f %f]\n", image->width, -image->height, -x/scale, image->height+y/scale);
5321 		ps_printf(psdoc, "{ rpicstr readstring }\n");
5322 		ps_printf(psdoc, "{ gpicstr readstring }\n");
5323 		ps_printf(psdoc, "{ bpicstr readstring }\n");
5324 		ps_printf(psdoc, "true %d\n", image->components);
5325 		ps_printf(psdoc, "colorimage\n");
5326 		rowptr = image->data;
5327 		dataptr = image->data;
5328 		for(j=0; j<image->height; j++) {
5329 			for(k=0; k<image->components; k++) {
5330 				dataptr = rowptr+k;
5331 				for(i=0; i<image->width; i++) {
5332 					ps_printf(psdoc, "%02x", *dataptr);
5333 					dataptr += 3;
5334 				}
5335 				ps_printf(psdoc, "\n");
5336 			}
5337 			rowptr += image->components*image->width;
5338 		}
5339 #else
5340 		ps_printf(psdoc, "%.2f %.2f translate\n", x, y);
5341 		ps_printf(psdoc, "%.2f %.2f scale\n", scale*image->width, scale*image->height);
5342 
5343 		imgenc = PS_get_parameter(psdoc, "imageencoding", 0.0);
5344 
5345 		if(image->isreusable)
5346 			ps_printf(psdoc, "%s 0 setfileposition\n", image->name);
5347 
5348 		if(!image->ismask) {
5349 			/* Output the image data */
5350 			switch(image->colorspace) {
5351 				case PS_COLORSPACE_GRAY:
5352 					ps_printf(psdoc, "/DeviceGray setcolorspace\n");
5353 					break;
5354 				case PS_COLORSPACE_RGB:
5355 					ps_printf(psdoc, "/DeviceRGB setcolorspace\n");
5356 					break;
5357 				case PS_COLORSPACE_CMYK:
5358 		  		ps_printf(psdoc, "/DeviceCMYK setcolorspace\n");
5359 					break;
5360 				case PS_COLORSPACE_INDEXED: {
5361 					int i;
5362 					ps_printf(psdoc, "[ /Indexed /DeviceRGB %d <\n", image->numcolors-1);
5363 					for(i=0; i<image->numcolors; i++) {
5364 						ps_printf(psdoc, "%02X", (int) ((image->palette[i].c1 * 255) + 0.5));
5365 						ps_printf(psdoc, "%02X", (int) ((image->palette[i].c2 * 255) + 0.5));
5366 						ps_printf(psdoc, "%02X", (int) ((image->palette[i].c3 * 255) + 0.5));
5367 						if((i+1)%8 == 0)
5368 							ps_printf(psdoc, "\n");
5369 						else
5370 							ps_printf(psdoc, " ");
5371 					}
5372 					ps_printf(psdoc, "> ] setcolorspace\n");
5373 					break;
5374 				}
5375 			}
5376 			ps_printf(psdoc, "<<\n");
5377 			ps_printf(psdoc, "  /ImageType 1\n");
5378 			ps_printf(psdoc, "  /Width %d\n", image->width);
5379 			ps_printf(psdoc, "  /Height %d\n", image->height);
5380 			ps_printf(psdoc, "  /BitsPerComponent %d\n", image->bpc);
5381 			switch(image->colorspace) {
5382 				case PS_COLORSPACE_GRAY:
5383 					ps_printf(psdoc, "  /Decode [0 1]\n");
5384 					break;
5385 				case PS_COLORSPACE_RGB:
5386 					ps_printf(psdoc, "  /Decode [0 1 0 1 0 1]\n");
5387 					break;
5388 				case PS_COLORSPACE_CMYK:
5389 					ps_printf(psdoc, "  /Decode [1 0 1 0 1 0 1 0]\n");
5390 					break;
5391 				case PS_COLORSPACE_INDEXED:
5392 					ps_printf(psdoc, "  /Decode [0 %d]\n", (int) pow(2,image->bpc)-1);
5393 					break;
5394 			}
5395 			ps_printf(psdoc, "  /ImageMatrix [%d 0 0 %d 0 %d]\n", image->width, -image->height, image->height);
5396 			if(image->isreusable) {
5397 				ps_printf(psdoc, "  /DataSource %s\n", image->name);
5398 				ps_printf(psdoc, ">> image\n");
5399 			} else {
5400 				ps_printf(psdoc, "  /DataSource currentfile /ASCII%sDecode filter\n", (imgenc == NULL || strcmp(imgenc, "hex") != 0) ? "85" : "Hex");
5401 				ps_printf(psdoc, ">> image\n");
5402 				if(image->components == 4 && image->colorspace == PS_COLORSPACE_RGB) {
5403 					dataptr = image->data;
5404 					reallength = image->height*image->width*3;
5405 					tmpdata = tmpdataptr = psdoc->malloc(psdoc, image->height*image->width*3, _("Allocate memory for temporary image data."));
5406 					for(j=0; j<image->length; j++) {
5407 						*tmpdataptr++ = *dataptr++;
5408 						*tmpdataptr++ = *dataptr++;
5409 						*tmpdataptr++ = *dataptr++;
5410 						dataptr++;
5411 					}
5412 				} else {
5413 					tmpdata = image->data;
5414 					reallength = image->length;
5415 				}
5416 				if(imgenc == NULL || strcmp(imgenc, "hex") != 0)
5417 					ps_ascii85_encode(psdoc, tmpdata, reallength);
5418 				else
5419 					ps_asciihex_encode(psdoc, tmpdata, reallength);
5420 				if(image->components == 4 && image->colorspace == PS_COLORSPACE_RGB)
5421 					psdoc->free(psdoc, tmpdata);
5422 			}
5423 		} else {
5424 			/* Output the image mask */
5425 			if(image->components == 4 && image->colorspace == PS_COLORSPACE_RGB) {
5426 				ps_printf(psdoc, "<<\n");
5427 				ps_printf(psdoc, "  /ImageType 1\n");
5428 				ps_printf(psdoc, "  /Width %d\n", image->width);
5429 				ps_printf(psdoc, "  /Height %d\n", image->height);
5430 				ps_printf(psdoc, "  /BitsPerComponent 1\n");
5431 				ps_printf(psdoc, "  /Decode [0 1]\n");
5432 				ps_printf(psdoc, "  /ImageMatrix [%d 0 0 %d 0 %d]\n", image->width, -image->height, image->height);
5433 				ps_printf(psdoc, "  /DataSource currentfile /ASCII%sDecode filter\n", (imgenc == NULL || strcmp(imgenc, "hex") != 0) ? "85" : "Hex");
5434 				ps_printf(psdoc, ">> imagemask\n");
5435 				dataptr = image->data;
5436 				tmpdata = tmpdataptr = psdoc->malloc(psdoc, (image->height*image->width/8)+1, _("Allocate memory for temporary image data."));
5437 				i = 0;
5438 				k = 0;
5439 				*tmpdataptr = 0;
5440 				for(j=0; j<image->height*image->width; j++) {
5441 					dataptr++; /* skip red */
5442 					dataptr++; /* skip green */
5443 					dataptr++; /* skip blue */
5444 					*tmpdataptr = *tmpdataptr << 1;
5445 					if(*dataptr & 0x80) {
5446 						*tmpdataptr += 1;
5447 					}
5448 					dataptr++;
5449 					i++;
5450 					if(i > 7) {
5451 						tmpdataptr++;
5452 						*tmpdataptr = 0;
5453 						i = 0;
5454 						k++;
5455 					}
5456 				}
5457 				if(i < 8 && i > 0) {
5458 					*tmpdataptr = *tmpdataptr << (8-i);
5459 					k++;
5460 				}
5461 				if(imgenc == NULL || strcmp(imgenc, "hex") != 0)
5462 					ps_ascii85_encode(psdoc, tmpdata, k);
5463 				else
5464 					ps_asciihex_encode(psdoc, tmpdata, k);
5465 				psdoc->free(psdoc, tmpdata);
5466 				ps_printf(psdoc, "\n");
5467 			}
5468 		}
5469 
5470 #endif
5471 		ps_printf(psdoc, "\n");
5472 		ps_printf(psdoc, "grestore\n");
5473 	} else if(0 == strncmp(image->type, "eps", 3)) {
5474 		PS_save(psdoc);
5475 		if(image->isreusable) {
5476 			PS_translate(psdoc, x, y);
5477 			PS_scale(psdoc, scale, scale);
5478 			ps_printf(psdoc, "/showpage{}N/erasepage{}N/copypage{}N\n");
5479 			ps_printf(psdoc, "%s 0 setfileposition %s cvx exec\n", image->name, image->name);
5480 		} else {
5481 			ps_printf(psdoc, "/showpage{}N/erasepage{}N/copypage{}N\n");
5482 			PS_translate(psdoc, x, y);
5483 			PS_scale(psdoc, scale, scale);
5484 			ps_write(psdoc, image->data, image->length);
5485 		}
5486 		PS_restore(psdoc);
5487 	} else if(0 == strcmp(image->type, "template")) {
5488 		PS_save(psdoc);
5489 		PS_translate(psdoc, x, y);
5490 		PS_scale(psdoc, scale, scale);
5491 		ps_printf(psdoc, "/%s /Form findresource execform\n", image->name);
5492 //		ps_printf(psdoc, "%s\n", image->name);
5493 		PS_restore(psdoc);
5494 	} else {
5495 		ps_error(psdoc, PS_RuntimeError, _("Images of type '%s' not supported."), image->type);
5496 	}
5497 	return;
5498 }
5499 /* }}} */
5500 
5501 /* PS_close_image() {{{
5502  * Free the memory used by an image
5503  */
5504 PSLIB_API void PSLIB_CALL
5505 PS_close_image(PSDoc *psdoc, int imageid) {
5506 	if(NULL == psdoc) {
5507 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
5508 		return;
5509 	}
5510 	/*
5511 	if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE)) {
5512 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' or 'page' scope."), __FUNCTION__);
5513 		return;
5514 	}
5515 	*/
5516 
5517 	_ps_unregister_image(psdoc, imageid);
5518 }
5519 /* }}} */
5520 
5521 /* PS_begin_template() {{{
5522  * starts a new template
5523  */
5524 PSLIB_API int PSLIB_CALL
5525 PS_begin_template(PSDoc *psdoc, float width, float height) {
5526 	PSImage *pstemplate;
5527 	char buffer[20];
5528 	int templateid;
5529 
5530 	buffer[0] = '\0';
5531 	if(NULL == psdoc) {
5532 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
5533 		return(0);
5534 	}
5535 	/* If the header is not written, because we are before
5536 	 * the first page, then output the header first.
5537 	 */
5538 	if(psdoc->beginprologwritten == ps_false) {
5539 		ps_write_ps_comments(psdoc);
5540 		ps_write_ps_beginprolog(psdoc);
5541 	}
5542 	if(ps_check_scope(psdoc, PS_SCOPE_DOCUMENT)) {
5543 		ps_error(psdoc, PS_Warning, _("Calling %s between pages is likely to cause problems when viewing the document. Call it before the first page."), __FUNCTION__);
5544 	}
5545 	if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PROLOG)) {
5546 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' scope."), __FUNCTION__);
5547 		return(0);
5548 	}
5549 
5550 	if(NULL == (pstemplate = (PSImage *) psdoc->malloc(psdoc, sizeof(PSImage), _("Allocate memory for template.")))) {
5551 		ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for template."));
5552 		return(0);
5553 	}
5554 	memset(pstemplate, 0, sizeof(PSImage));
5555 	if(0 == (templateid = _ps_register_image(psdoc, pstemplate))) {
5556 		ps_error(psdoc, PS_MemoryError, _("Could not register template."));
5557 		psdoc->free(psdoc, pstemplate);
5558 		return(0);
5559 	}
5560 
5561 	sprintf(buffer, "template%d", templateid);
5562 	pstemplate->psdoc = psdoc;
5563 	pstemplate->name = ps_strdup(psdoc, buffer);
5564 	pstemplate->type = ps_strdup(psdoc, "template");
5565 	pstemplate->data = NULL;
5566 	pstemplate->width = width;
5567 	pstemplate->height = height;
5568 
5569 //	ps_printf(psdoc, "currentglobal true setglobal\n");
5570 	ps_printf(psdoc, "/%s << /FormType 1 ", buffer);
5571 	ps_printf(psdoc, "/BBox [0 0 %f %f] ", width, height);
5572 	ps_printf(psdoc, "/Matrix [1 0 0 1 0 0] ");
5573 	ps_printf(psdoc, "/PaintProc { pop\n");
5574 
5575 //	ps_printf(psdoc, "/%s {\n", buffer);
5576 	ps_enter_scope(psdoc, PS_SCOPE_TEMPLATE);
5577 
5578 	return(templateid);
5579 }
5580 /* }}} */
5581 
5582 /* PS_end_template() {{{
5583  * ends a template
5584  */
5585 PSLIB_API void PSLIB_CALL
5586 PS_end_template(PSDoc *psdoc) {
5587 	if(NULL == psdoc) {
5588 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
5589 		return;
5590 	}
5591 	if(!ps_check_scope(psdoc, PS_SCOPE_TEMPLATE)) {
5592 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'template' scope."), __FUNCTION__);
5593 		return;
5594 	}
5595 
5596 	ps_printf(psdoc, "} >> /Form defineresource pop %% setglobal\n");
5597 //	ps_printf(psdoc, "} B\n");
5598 
5599 	ps_leave_scope(psdoc, PS_SCOPE_TEMPLATE);
5600 }
5601 /* }}} */
5602 
5603 /* PS_begin_pattern() {{{
5604  * starts a new pattern
5605  */
5606 PSLIB_API int PSLIB_CALL
5607 PS_begin_pattern(PSDoc *psdoc, float width, float height, float xstep, float ystep, int painttype) {
5608 	PSPattern *pspattern;
5609 	char buffer[20];
5610 	int patternid;
5611 
5612 	buffer[0] = '\0';
5613 	if(NULL == psdoc) {
5614 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
5615 		return(0);
5616 	}
5617 	if(painttype < 1 || painttype > 2) {
5618 		ps_error(psdoc, PS_RuntimeError, _("Painttype must be 1 or 2."));
5619 		return(0);
5620 	}
5621 
5622 	/* If the header is not written, because we are before
5623 	 * the first page, then output the header first.
5624 	 */
5625 	if(psdoc->beginprologwritten == ps_false) {
5626 		ps_write_ps_comments(psdoc);
5627 		ps_write_ps_beginprolog(psdoc);
5628 	}
5629 	if(ps_check_scope(psdoc, PS_SCOPE_DOCUMENT)) {
5630 		ps_error(psdoc, PS_Warning, _("Calling %s between pages is likely to cause problems when viewing the document. Call it before the first page."), __FUNCTION__);
5631 	}
5632 	if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PROLOG)) {
5633 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' scope."), __FUNCTION__);
5634 		return(0);
5635 	}
5636 
5637 	if(NULL == (pspattern = (PSPattern *) psdoc->malloc(psdoc, sizeof(PSPattern), _("Allocate memory for pattern.")))) {
5638 		ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for pattern."));
5639 		return(0);
5640 	}
5641 	memset(pspattern, 0, sizeof(PSPattern));
5642 	psdoc->pattern = pspattern;
5643 	if(0 == (patternid = _ps_register_pattern(psdoc, pspattern))) {
5644 		ps_error(psdoc, PS_MemoryError, _("Could not register pattern."));
5645 		psdoc->free(psdoc, pspattern);
5646 		return(0);
5647 	}
5648 
5649 	sprintf(buffer, "pattern%d", patternid);
5650 	pspattern->psdoc = psdoc;
5651 	pspattern->name = ps_strdup(psdoc, buffer);
5652 	pspattern->painttype = painttype;
5653 	pspattern->xstep = xstep;
5654 	pspattern->ystep = ystep;
5655 	pspattern->width = width;
5656 	pspattern->height = height;
5657 
5658 	ps_printf(psdoc, "<< /PatternType 1 ");
5659 	ps_printf(psdoc, "/BBox [0 0 %f %f] ", width, height);
5660 	ps_printf(psdoc, "/XStep %f ", xstep);
5661 	ps_printf(psdoc, "/YStep %f ", ystep);
5662 	ps_printf(psdoc, "/PaintType %d ", painttype);
5663 	ps_printf(psdoc, "/TilingType 1 ");
5664 	ps_printf(psdoc, "/PaintProc { begin \n");
5665 
5666 	ps_enter_scope(psdoc, PS_SCOPE_PATTERN);
5667 
5668 	return(patternid);
5669 }
5670 /* }}} */
5671 
5672 /* PS_end_pattern() {{{
5673  * ends a pattern
5674  */
5675 PSLIB_API void PSLIB_CALL
5676 PS_end_pattern(PSDoc *psdoc) {
5677 	if(NULL == psdoc) {
5678 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
5679 		return;
5680 	}
5681 	if(!ps_check_scope(psdoc, PS_SCOPE_PATTERN)) {
5682 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'pattern' scope."), __FUNCTION__);
5683 		return;
5684 	}
5685 
5686 	ps_printf(psdoc, " end } bind >> matrix makepattern /%s exch def\n", psdoc->pattern->name);
5687 
5688 	ps_leave_scope(psdoc, PS_SCOPE_PATTERN);
5689 }
5690 /* }}} */
5691 
5692 /* PS_shading_pattern() {{{
5693  * Creates a shading pattern
5694  */
5695 PSLIB_API int PSLIB_CALL
5696 PS_shading_pattern(PSDoc *psdoc, int shading, const char *optlist) {
5697 	PSPattern *pspattern;
5698 	char buffer[20];
5699 	int patternid;
5700 	PSShading *psshading;
5701 
5702 	buffer[0] = '\0';
5703 	if(NULL == psdoc) {
5704 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
5705 		return 0;
5706 	}
5707 	if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE)) {
5708 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' or 'page' scope."), __FUNCTION__);
5709 		return 0;
5710 	}
5711 	psshading = _ps_get_shading(psdoc, shading);
5712 	if(NULL == psshading) {
5713 		ps_error(psdoc, PS_RuntimeError, _("PSShading is null."));
5714 		return 0;
5715 	}
5716 	if(NULL == (pspattern = (PSPattern *) psdoc->malloc(psdoc, sizeof(PSPattern), _("Allocate memory for pattern.")))) {
5717 		ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for pattern."));
5718 		return(0);
5719 	}
5720 	memset(pspattern, 0, sizeof(PSPattern));
5721 	psdoc->pattern = pspattern;
5722 	if(0 == (patternid = _ps_register_pattern(psdoc, pspattern))) {
5723 		ps_error(psdoc, PS_MemoryError, _("Could not register pattern."));
5724 		psdoc->free(psdoc, pspattern);
5725 		return(0);
5726 	}
5727 
5728 	sprintf(buffer, "pattern%d", patternid);
5729 	pspattern->psdoc = psdoc;
5730 	pspattern->name = ps_strdup(psdoc, buffer);
5731 	pspattern->painttype = 1;
5732 
5733 	ps_printf(psdoc, "<< /PatternType 2 ", buffer);
5734 	ps_printf(psdoc, "  /Shading\n", buffer);
5735 	ps_output_shading_dict(psdoc, psshading);
5736 	ps_printf(psdoc, ">> matrix makepattern /%s exch def\n", pspattern->name);
5737 	return(patternid);
5738 }
5739 /* }}} */
5740 
5741 /* PS_shading() {{{
5742  * Creates a shading object
5743  */
5744 PSLIB_API int PSLIB_CALL
5745 PS_shading(PSDoc *psdoc, const char *shtype, float x0, float y0, float x1, float y1, float c1, float c2, float c3, float c4, const char *optlist) {
5746 	PSShading *psshading;
5747 	int shadingid;
5748 	float optlist_N = 1.0, optlist_r0 = 0.0, optlist_r1 = 0.0;
5749 	ps_bool optlist_extend0 = ps_false, optlist_extend1 = ps_false;
5750 	ps_bool optlist_antialias = 0;
5751 
5752 	if(NULL == psdoc) {
5753 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
5754 		return(0);
5755 	}
5756 	if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE)) {
5757 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' or 'page' scope."), __FUNCTION__);
5758 		return(0);
5759 	}
5760 
5761 	if(psdoc->agstates[psdoc->agstate].fillcolor.colorspace == PS_COLORSPACE_PATTERN) {
5762 		ps_error(psdoc, PS_RuntimeError, _("Current fill color is a pattern which cannot be used for shading."));
5763 		return(0);
5764 	}
5765 
5766 	if(psdoc->agstates[psdoc->agstate].fillcolor.colorspace == PS_COLORSPACE_SPOT &&
5767 	  psdoc->agstates[psdoc->agstate].fillcolor.c1 != c1) {
5768 		ps_error(psdoc, PS_RuntimeError, _("The current fill spot color is not the same color as the one set for shading."));
5769 		return(0);
5770 	}
5771 	// FIXME: There must be some checking, if the passed spot color
5772 	// is in the same as the fill color set before.
5773 	if(NULL == (psshading = (PSShading *) psdoc->malloc(psdoc, sizeof(PSShading), _("Allocate memory for pattern.")))) {
5774 		ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for shading."));
5775 		return(0);
5776 	}
5777 	memset(psshading, 0, sizeof(PSShading));
5778 
5779 	/* Read the option list only, if it is non empty. */
5780 	if(NULL != optlist && optlist[0] != '\0') {
5781 		ght_hash_table_t *opthash;
5782 		opthash = ps_parse_optlist(psdoc, optlist);
5783 		if(NULL == opthash) {
5784 			ps_error(psdoc, PS_RuntimeError, _("Error while parsing option list."));
5785 			return(0);
5786 		}
5787 		/* Issue a warning only if a value is given but cannot be parsed */
5788 		if(-2 == get_optlist_element_as_float(psdoc, opthash, "N", &optlist_N)) {
5789 			ps_error(psdoc, PS_Warning, _("Value of option list element 'N' could not be read, using default."));
5790 		}
5791 		if(-2 == get_optlist_element_as_bool(psdoc, opthash, "extend0", &optlist_extend0)) {
5792 			ps_error(psdoc, PS_Warning, _("Value of option list element 'extend0' could not be read, using default."));
5793 		}
5794 		if(-2 == get_optlist_element_as_bool(psdoc, opthash, "extend1", &optlist_extend1)) {
5795 			ps_error(psdoc, PS_Warning, _("Value of option list element 'extend1' could not be read, using default."));
5796 		}
5797 		if(-2 == get_optlist_element_as_bool(psdoc, opthash, "antialias", &optlist_antialias)) {
5798 			ps_error(psdoc, PS_Warning, _("Value of option list element 'antialias' could not be read, using default."));
5799 		}
5800 		if(strcmp(shtype, "radial") == 0) {
5801 			if(0 > get_optlist_element_as_float(psdoc, opthash, "r0", &optlist_r0)) {
5802 				ps_error(psdoc, PS_RuntimeError, _("Could not retrieve required parameter 'r0' from option list."));
5803 				return(0);
5804 			}
5805 			if(0 > get_optlist_element_as_float(psdoc, opthash, "r1", &optlist_r1)) {
5806 				ps_error(psdoc, PS_RuntimeError, _("Could not retrieve required parameter 'r1' from option list."));
5807 				return(0);
5808 			}
5809 		}
5810 		ps_free_optlist(psdoc, opthash);
5811 	} else if(strcmp(shtype, "radial") == 0) {
5812 		ps_error(psdoc, PS_RuntimeError, _("If type of shading is 'radial' the parameters 'r0' and 'r1' must be set in the option list."));
5813 		return(0);
5814 	}
5815 
5816 	if(strcmp(shtype, "axial") == 0) {
5817 		psshading->type = 2;
5818 	} else if(strcmp(shtype, "radial") == 0) {
5819 		psshading->type = 3;
5820 	} else {
5821 		ps_error(psdoc, PS_RuntimeError, _("Type of shading must be 'radial' or 'axial'."));
5822 		return(0);
5823 	}
5824 	psshading->x0 = x0;
5825 	psshading->y0 = y0;
5826 	psshading->x1 = x1;
5827 	psshading->y1 = y1;
5828 	psshading->r0 = optlist_r0;
5829 	psshading->r1 = optlist_r1;
5830 	psshading->N = optlist_N;
5831 	psshading->extend0 = optlist_extend0;
5832 	psshading->extend1 = optlist_extend1;
5833 	psshading->antialias = optlist_antialias;
5834 	psshading->startcolor.colorspace = psdoc->agstates[psdoc->agstate].fillcolor.colorspace;
5835 	psshading->startcolor.prevcolorspace = psdoc->agstates[psdoc->agstate].fillcolor.prevcolorspace;
5836 	psshading->startcolor.pattern = psdoc->agstates[psdoc->agstate].fillcolor.pattern;
5837 	psshading->startcolor.c1 = psdoc->agstates[psdoc->agstate].fillcolor.c1;
5838 	psshading->startcolor.c2 = psdoc->agstates[psdoc->agstate].fillcolor.c2;
5839 	psshading->startcolor.c3 = psdoc->agstates[psdoc->agstate].fillcolor.c3;
5840 	psshading->startcolor.c4 = psdoc->agstates[psdoc->agstate].fillcolor.c4;
5841 	psshading->endcolor.colorspace = psshading->startcolor.colorspace;
5842 	psshading->endcolor.prevcolorspace = 0;
5843 	psshading->endcolor.pattern = 0;
5844 	psshading->endcolor.c1 = c1;
5845 	psshading->endcolor.c2 = c2;
5846 	psshading->endcolor.c3 = c3;
5847 	psshading->endcolor.c4 = c4;
5848 
5849 	if(0 == (shadingid = _ps_register_shading(psdoc, psshading))) {
5850 		ps_error(psdoc, PS_MemoryError, _("Could not register shading."));
5851 		psdoc->free(psdoc, psshading);
5852 		return(0);
5853 	}
5854 	return(shadingid);
5855 }
5856 /* }}} */
5857 
5858 /* PS_begin_font() {{{
5859  * starts a new font
5860  */
5861 PSLIB_API int PSLIB_CALL
5862 PS_begin_font(PSDoc *psdoc, const char *fontname, int reserverd, double a, double b, double c, double d, double e, double f, const char *optlist) {
5863 	PSFont *psfont;
5864 	ENCODING *fontenc;
5865 	ADOBEFONTMETRIC *metrics;
5866 	char buffer[20];
5867 	int fontid, i;
5868 
5869 	buffer[0] = '\0';
5870 	if(NULL == psdoc) {
5871 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
5872 		return(0);
5873 	}
5874 
5875 	/* If the header is not written, because we are before
5876 	 * the first page, then output the header first.
5877 	 */
5878 	if(psdoc->beginprologwritten == ps_false) {
5879 		ps_write_ps_comments(psdoc);
5880 		ps_write_ps_beginprolog(psdoc);
5881 	}
5882 	if(ps_check_scope(psdoc, PS_SCOPE_DOCUMENT)) {
5883 		ps_error(psdoc, PS_Warning, _("Calling %s between pages is likely to cause problems when viewing the document. Call it before the first page."), __FUNCTION__);
5884 	}
5885 	if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PROLOG)) {
5886 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' scope."), __FUNCTION__);
5887 		return(0);
5888 	}
5889 
5890 	if(NULL == (psfont = (PSFont *) psdoc->malloc(psdoc, sizeof(PSFont), _("Allocate memory for font.")))) {
5891 		ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for font."));
5892 		return(0);
5893 	}
5894 	memset(psfont, 0, sizeof(PSFont));
5895 
5896 	if(NULL == (metrics = (ADOBEFONTMETRIC *) psdoc->malloc(psdoc, sizeof(ADOBEFONTMETRIC), _("Allocate space for font metric.")))) {
5897 		return(0);
5898 	}
5899 	memset(metrics, 0, sizeof(ADOBEFONTMETRIC));
5900 	metrics->codingscheme = ps_strdup(psdoc, "FontSpecific");
5901 	metrics->fontname = ps_strdup(psdoc, fontname);
5902 	metrics->fontenc = NULL;
5903 	metrics->gadobechars = ght_create(512);
5904 	ght_set_alloc(metrics->gadobechars, ps_ght_malloc, ps_ght_free, psdoc);
5905 	readencoding(psdoc, metrics, NULL);
5906 	psfont->encoding = ps_strdup(psdoc, "default");
5907 	psfont->metrics = metrics;
5908 	psdoc->font = psfont;
5909 
5910 	if(0 == (fontid = _ps_register_font(psdoc, psfont))) {
5911 		ps_error(psdoc, PS_MemoryError, _("Could not register font."));
5912 		psdoc->free(psdoc, psfont);
5913 		return(0);
5914 	}
5915 
5916 	psfont->psdoc = psdoc;
5917 
5918 	ps_printf(psdoc, "8 dict begin\n");
5919 	ps_printf(psdoc, "  /FontType 3 def\n");
5920 	ps_printf(psdoc, "  /FontMatrix [%f %f %f %f %f %f] def\n", a, b, c, d, e, f);
5921 //	ps_printf(psdoc, "  /FontName %s \n", fontname);
5922 	ps_printf(psdoc, "  /FontBBox [0 0 750 750] def\n");
5923 	ps_printf(psdoc, "  /Encoding 256 array def 0 1 255 {Encoding exch /.notdef put} for\n");
5924 	fontenc = &fontencoding[0];
5925 	for(i=0; i<255; i++) {
5926 		if((fontenc->vec[i] != NULL) && (*(fontenc->vec[i]) != '\0')) {
5927 				ps_printf(psdoc, "  Encoding %d /%s put\n", i, fontenc->vec[i]);
5928 		}
5929 	}
5930 	ps_printf(psdoc, "  /BuildGlyph\n");
5931 	ps_printf(psdoc, "    { %%1000 0\n");
5932 	ps_printf(psdoc, "      %%0 0 750 750\n");
5933 	ps_printf(psdoc, "      %%setcachedevice\n");
5934 	ps_printf(psdoc, "      exch /CharProcs get exch\n");
5935 	ps_printf(psdoc, "      2 copy known not { pop /.notdef} if\n");
5936 	ps_printf(psdoc, "      get exec\n");
5937 	ps_printf(psdoc, "    } bind def\n");
5938 	ps_printf(psdoc, "  /BuildChar\n");
5939 	ps_printf(psdoc, "    { 1 index /Encoding get exch get\n");
5940 	ps_printf(psdoc, "      1 index /BuildGlyph get exec\n");
5941 	ps_printf(psdoc, "    } bind def\n");
5942 //	ps_printf(psdoc, "  currentdict\n");
5943 	ps_printf(psdoc, "  /CharProcs 255 dict def\n");
5944 	ps_printf(psdoc, "    CharProcs begin\n");
5945 	ps_printf(psdoc, "      /.notdef { } def\n");
5946 
5947 	ps_enter_scope(psdoc, PS_SCOPE_FONT);
5948 
5949 	return(fontid);
5950 }
5951 /* }}} */
5952 
5953 /* PS_end_font() {{{
5954  * ends a font
5955  */
5956 PSLIB_API void PSLIB_CALL
5957 PS_end_font(PSDoc *psdoc) {
5958 	PSFont *psfont;
5959 
5960 	if(NULL == psdoc) {
5961 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
5962 		return;
5963 	}
5964 	if(!ps_check_scope(psdoc, PS_SCOPE_FONT)) {
5965 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'font' scope."), __FUNCTION__);
5966 		return;
5967 	}
5968 
5969 	psfont = psdoc->font;
5970 	ps_printf(psdoc, "    end\n");
5971 	ps_printf(psdoc, "  currentdict\n");
5972 	ps_printf(psdoc, "end\n");
5973 	ps_printf(psdoc, "/%s exch definefont pop\n", psfont->metrics->fontname);
5974 
5975 	ps_leave_scope(psdoc, PS_SCOPE_FONT);
5976 }
5977 /* }}} */
5978 
5979 /* PS_begin_glyph() {{{
5980  * starts a new glyph
5981  */
5982 PSLIB_API int PSLIB_CALL
5983 PS_begin_glyph(PSDoc *psdoc, const char *glyphname, double wx, double llx, double lly, double urx, double ury) {
5984 	ADOBEINFO *ai;
5985 	PSFont *psfont;
5986 
5987 	if(NULL == psdoc) {
5988 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
5989 		return 0;
5990 	}
5991 	if(!ps_check_scope(psdoc, PS_SCOPE_FONT)) {
5992 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'font' scope."), __FUNCTION__);
5993 		return 0;
5994 	}
5995 
5996 	psfont = psdoc->font;
5997 	if(ai = gfindadobe(psfont->metrics->gadobechars, glyphname)) {
5998 		ps_error(psdoc, PS_RuntimeError, _("Font already contains glyph with name '%s'."), glyphname);
5999 		return 0;
6000 	}
6001 
6002 	ai = (ADOBEINFO *) psdoc->malloc(psdoc, (unsigned long) sizeof(ADOBEINFO), "newchar: allocate memory for new characters") ;
6003 	ai->adobenum = -1 ;
6004 	ai->texnum = -1 ;
6005 	ai->adobename = ps_strdup(psdoc, glyphname);
6006 	ai->llx = llx;
6007 	ai->lly = lly;
6008 	ai->urx = urx;
6009 	ai->ury = ury;
6010 	ai->lkern = 0;
6011 	ai->rkern = 0;
6012 	ai->ligs = NULL ;
6013 	ai->kerns = NULL ;
6014 	ai->kern_equivs = NULL ;
6015 	ai->pccs = NULL ;
6016 	ai->width = wx;
6017 	ght_insert(psfont->metrics->gadobechars, ai, strlen(glyphname)+1, (void *) glyphname);
6018 
6019 	ps_printf(psdoc, "      /%s {\n", glyphname);
6020 	ps_printf(psdoc, "      %.4f 0 %.4f %.4f %.4f %.4f setcachedevice\n", (float) wx, (float) llx, (float) lly, (float) urx, (float) ury);
6021 	ps_enter_scope(psdoc, PS_SCOPE_GLYPH);
6022 	return 1;
6023 }
6024 /* }}} */
6025 
6026 /* PS_end_glyph() {{{
6027  * ends a glyph
6028  */
6029 PSLIB_API void PSLIB_CALL
6030 PS_end_glyph(PSDoc *psdoc) {
6031 	if(NULL == psdoc) {
6032 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6033 		return;
6034 	}
6035 	if(!ps_check_scope(psdoc, PS_SCOPE_GLYPH)) {
6036 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'glyph' scope."), __FUNCTION__);
6037 		return;
6038 	}
6039 
6040 	ps_printf(psdoc, "      } bind def\n");
6041 
6042 	ps_leave_scope(psdoc, PS_SCOPE_GLYPH);
6043 }
6044 /* }}} */
6045 
6046 /* PS_add_kerning() {{{
6047  * adds kerning for pair of glyphs
6048  */
6049 PSLIB_API void PSLIB_CALL
6050 PS_add_kerning(PSDoc *psdoc, int fontid, const char *glyphname1, const char *glyphname2, int kern) {
6051 	ADOBEINFO *ai1, *ai2;
6052 	PSFont *psfont;
6053 
6054 	if(NULL == psdoc) {
6055 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6056 		return;
6057 	}
6058 	if(!ps_check_scope(psdoc, PS_SCOPE_FONT)) {
6059 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'font' scope."), __FUNCTION__);
6060 		return;
6061 	}
6062 
6063 	if(0 == fontid)
6064 		psfont = psdoc->font;
6065 	else {
6066 		if(NULL == (psfont = _ps_get_font(psdoc, fontid)))
6067 			return;
6068 	}
6069 
6070 	ai1 = gfindadobe(psfont->metrics->gadobechars, glyphname1);
6071 	if(!ai1) {
6072 		ps_error(psdoc, PS_RuntimeError, _("First glyph '%s' of kerning pair does not exist in font."), glyphname1);
6073 		return;
6074 	}
6075 	ai2 = gfindadobe(psfont->metrics->gadobechars, glyphname2);
6076 	if(!ai2) {
6077 		ps_error(psdoc, PS_RuntimeError, _("Second glyph '%s' of kerning pair does not exist in font."), glyphname2);
6078 		return;
6079 	}
6080 	if(calculatekern(ai1, ai2) != 0) {
6081 		ps_error(psdoc, PS_RuntimeError, _("Kerning pair (%s, %s) already exists in font."), glyphname1, glyphname2);
6082 	}
6083 	addkern(psdoc, ai1, ai2, kern);
6084 }
6085 /* }}} */
6086 
6087 /* PS_add_ligature() {{{
6088  * adds ligature for pair of glyphs
6089  */
6090 PSLIB_API void PSLIB_CALL
6091 PS_add_ligature(PSDoc *psdoc, int fontid, const char *glyphname1, const char *glyphname2, const char *glyphname3) {
6092 	ADOBEINFO *ai1, *ai2, *ai3;
6093 	PSFont *psfont;
6094 
6095 	if(NULL == psdoc) {
6096 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6097 		return;
6098 	}
6099 	if(!ps_check_scope(psdoc, PS_SCOPE_FONT)) {
6100 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'font' scope."), __FUNCTION__);
6101 		return;
6102 	}
6103 
6104 	if(0 == fontid)
6105 		psfont = psdoc->font;
6106 	else {
6107 		if(NULL == (psfont = _ps_get_font(psdoc, fontid)))
6108 			return;
6109 	}
6110 
6111 	ai1 = gfindadobe(psfont->metrics->gadobechars, glyphname1);
6112 	if(!ai1) {
6113 		ps_error(psdoc, PS_RuntimeError, _("First glyph '%s' of ligature does not exist in font."), glyphname1);
6114 		return;
6115 	}
6116 	ai2 = gfindadobe(psfont->metrics->gadobechars, glyphname2);
6117 	if(!ai2) {
6118 		ps_error(psdoc, PS_RuntimeError, _("Successor glyph '%s' of ligature does not exist in font."), glyphname2);
6119 		return;
6120 	}
6121 	ai3 = gfindadobe(psfont->metrics->gadobechars, glyphname3);
6122 	if(!ai3) {
6123 		ps_error(psdoc, PS_RuntimeError, _("Substitute glyph '%s' of ligature does not exist in font."), glyphname3);
6124 		return;
6125 	}
6126 	addligature(psdoc, ai1, ai2, ai3);
6127 }
6128 /* }}} */
6129 
6130 /* PS_get_buffer() {{{
6131  * Returns a pointer to the current buffer and sets its size to 0
6132  */
6133 PSLIB_API const char * PSLIB_CALL
6134 PS_get_buffer(PSDoc *psdoc, long *size) {
6135 	const char *tmp;
6136 	if(NULL == psdoc) {
6137 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6138 		return(NULL);
6139 	}
6140 	if(NULL == psdoc->sb) {
6141 		*size = 0;
6142 		return(NULL);
6143 	}
6144 	*size = str_buffer_len(psdoc, psdoc->sb);
6145 	tmp = str_buffer_get(psdoc, psdoc->sb);
6146 	str_buffer_clear(psdoc, psdoc->sb);
6147 	return(tmp);
6148 }
6149 /* }}} */
6150 
6151 /* PS_set_border_style() {{{
6152  * Sets style of border for link destination
6153  */
6154 PSLIB_API void PSLIB_CALL
6155 PS_set_border_style(PSDoc *psdoc, const char *style, float width) {
6156 	if(NULL == psdoc) {
6157 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6158 		return;
6159 	}
6160 	if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE)) {
6161 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' or 'page' scope."), __FUNCTION__);
6162 		return;
6163 	}
6164 
6165 	psdoc->border_width = width;
6166 	if(0 == strcmp(style, "solid"))
6167 		psdoc->border_style = PS_BORDER_SOLID;
6168 	else if(0 == strcmp(style, "dashed")) {
6169 		psdoc->border_style = PS_BORDER_DASHED;
6170 		psdoc->border_white = psdoc->border_black = 3.0;
6171 	} else
6172 		ps_error(psdoc, PS_RuntimeError, _("Parameter style of PS_set_border_style() must be 'solid' or 'dashed'\n"));
6173 }
6174 /* }}} */
6175 
6176 /* PS_set_border_color() {{{
6177  * Sets color of border for link destination
6178  */
6179 PSLIB_API void PSLIB_CALL
6180 PS_set_border_color(PSDoc *psdoc, float red, float green, float blue) {
6181 	if(NULL == psdoc) {
6182 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6183 		return;
6184 	}
6185 	if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE)) {
6186 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' or 'page' scope."), __FUNCTION__);
6187 		return;
6188 	}
6189 
6190 	psdoc->border_red = red;
6191 	psdoc->border_green = green;
6192 	psdoc->border_blue = blue;
6193 }
6194 /* }}} */
6195 
6196 /* PS_set_border_dash() {{{
6197  * Sets dash of border for link destination
6198  */
6199 PSLIB_API void PSLIB_CALL
6200 PS_set_border_dash(PSDoc *psdoc, float black, float white) {
6201 	if(NULL == psdoc) {
6202 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6203 		return;
6204 	}
6205 	if(!ps_check_scope(psdoc, PS_SCOPE_DOCUMENT|PS_SCOPE_PAGE)) {
6206 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'document' or 'page' scope."), __FUNCTION__);
6207 		return;
6208 	}
6209 
6210 	psdoc->border_black = black;
6211 	psdoc->border_white = white;
6212 }
6213 /* }}} */
6214 
6215 /* _ps_output_anno_border() {{{
6216  *
6217  */
6218 void static
6219 _ps_output_anno_border(PSDoc *psdoc) {
6220 	switch(psdoc->border_style) {
6221 		case PS_BORDER_SOLID:
6222 			ps_printf(psdoc, "/Border [ %f 1 1 ] ", psdoc->border_width);
6223 			break;
6224 		case PS_BORDER_DASHED:
6225 			ps_printf(psdoc, "/Border [ %f %f %f ] ", psdoc->border_black, psdoc->border_white, psdoc->border_width);
6226 			break;
6227 	}
6228 	ps_printf(psdoc, "/Color [ %f %f %f ] ", psdoc->border_red, psdoc->border_green, psdoc->border_blue);
6229 }
6230 /* }}} */
6231 
6232 /* PS_add_weblink() {{{
6233  * Adds a link to an external web page
6234  */
6235 PSLIB_API void PSLIB_CALL
6236 PS_add_weblink(PSDoc *psdoc, float llx, float lly, float urx, float ury, const char *url) {
6237 	if(NULL == psdoc) {
6238 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6239 		return;
6240 	}
6241 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE)) {
6242 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page' scope."), __FUNCTION__);
6243 		return;
6244 	}
6245 
6246 	ps_printf(psdoc, "[ /Rect [ %f %f %f %f] ", llx, lly, urx, ury);
6247 	_ps_output_anno_border(psdoc);
6248 	ps_printf(psdoc, "/Action << /Subtype /URI /URI (%s) >> /Subtype /Link /ANN pdfmark\n", url);
6249 }
6250 /* }}} */
6251 
6252 /* PS_add_pdflink() {{{
6253  * Adds a link to external pdf document
6254  */
6255 PSLIB_API void PSLIB_CALL
6256 PS_add_pdflink(PSDoc *psdoc, float llx, float lly, float urx, float ury, const char *filename, int page, const char *dest) {
6257 	if(NULL == psdoc) {
6258 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6259 		return;
6260 	}
6261 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE)) {
6262 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page' scope."), __FUNCTION__);
6263 		return;
6264 	}
6265 
6266 	ps_printf(psdoc, "[ /Rect [ %f %f %f %f] ", llx, lly, urx, ury);
6267 	_ps_output_anno_border(psdoc);
6268 	ps_printf(psdoc, "/Page %d ", page);
6269 	if(0 == strcmp(dest, "fitpage"))
6270 		ps_printf(psdoc, "/View %s ", "[ /Fit ]");
6271 	else if(0 == strcmp(dest, "fitwidth"))
6272 		ps_printf(psdoc, "/View %s ", "[ /FitH -32768 ]");
6273 	else if(0 == strcmp(dest, "fitheight"))
6274 		ps_printf(psdoc, "/View %s ", "[ /FitV -32768 ]");
6275 	else if(0 == strcmp(dest, "fitbbox"))
6276 		ps_printf(psdoc, "/View %s ", "/FitB");
6277 	else if(0 != strcmp(dest, "retain"))
6278 		ps_error(psdoc, PS_RuntimeError, _("Parameter dest of PS_add_pdflink() must be 'fitpage', 'fitwidth', 'fitheight', 'fitbbox', 'retain'."));
6279 	ps_printf(psdoc, "/Action /GoToR /File (%s) /Subtype /Link /ANN pdfmark\n", filename);
6280 }
6281 /* }}} */
6282 
6283 /* PS_add_locallink() {{{
6284  * Adds a link within the pdf document
6285  */
6286 PSLIB_API void PSLIB_CALL
6287 PS_add_locallink(PSDoc *psdoc, float llx, float lly, float urx, float ury, const int page, const char *dest) {
6288 	if(NULL == psdoc) {
6289 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6290 		return;
6291 	}
6292 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE)) {
6293 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page' scope."), __FUNCTION__);
6294 		return;
6295 	}
6296 
6297 	ps_printf(psdoc, "[ /Rect [ %f %f %f %f] ", llx, lly, urx, ury);
6298 	_ps_output_anno_border(psdoc);
6299 	if(page == PS_GOTO_NEXT_PAGE)
6300 		ps_printf(psdoc, "/Page /Next ");
6301 	else if(page == PS_GOTO_PREV_PAGE)
6302 		ps_printf(psdoc, "/Page /Prev ");
6303 	else
6304 		ps_printf(psdoc, "/Page %d ", page);
6305 	if(0 == strcmp(dest, "fitpage"))
6306 		ps_printf(psdoc, "/View %s ", "[ /Fit ]");
6307 	else if(0 == strcmp(dest, "fitwidth"))
6308 		ps_printf(psdoc, "/View %s ", "[ /FitH -32768 ]");
6309 	else if(0 == strcmp(dest, "fitheight"))
6310 		ps_printf(psdoc, "/View %s ", "[ /FitV -32768 ]");
6311 	else if(0 == strcmp(dest, "fitbbox"))
6312 		ps_printf(psdoc, "/View %s ", "/FitB");
6313 	else if(0 != strcmp(dest, "retain"))
6314 		ps_error(psdoc, PS_RuntimeError, _("Parameter dest of PS_add_locallink() must be 'fitpage', 'fitwidth', 'fitheight', 'fitbbox', 'retain'."));
6315 	ps_printf(psdoc, "/Subtype /Link /ANN pdfmark\n");
6316 }
6317 /* }}} */
6318 
6319 /* PS_add_launchlink() {{{
6320  * Adds a link to an external program or file
6321  */
6322 PSLIB_API void PSLIB_CALL
6323 PS_add_launchlink(PSDoc *psdoc, float llx, float lly, float urx, float ury, const char *filename) {
6324 	if(NULL == psdoc) {
6325 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6326 		return;
6327 	}
6328 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE)) {
6329 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page' scope."), __FUNCTION__);
6330 		return;
6331 	}
6332 
6333 	ps_printf(psdoc, "[ /Rect [ %f %f %f %f] ", llx, lly, urx, ury);
6334 	_ps_output_anno_border(psdoc);
6335 	ps_printf(psdoc, "/Action << /S /Launch /F (%s) >> /Subtype /Link /ANN pdfmark\n", filename);
6336 }
6337 /* }}} */
6338 
6339 /* PS_add_bookmark() {{{
6340  * Adds a bookmark (nested bookmarks are not yet support)
6341  */
6342 PSLIB_API int PSLIB_CALL
6343 PS_add_bookmark(PSDoc *psdoc, const char *text, int parent, int open) {
6344 	PS_BOOKMARK *bookmark, *parentbookmark;
6345 	DLIST *bl;
6346 
6347 	if(NULL == psdoc) {
6348 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6349 		return(0);
6350 	}
6351 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE)) {
6352 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page' scope."), __FUNCTION__);
6353 		return(0);
6354 	}
6355 
6356 	if(parent < 0 || parent > psdoc->lastbookmarkid) {
6357 		ps_error(psdoc, PS_RuntimeError, _("Parent bookmark ist out of possible range."));
6358 		return(0);
6359 	}
6360 	if(parent == 0) { /* Add bookmark on first level */
6361 		bl = psdoc->bookmarks;
6362 	} else {
6363 		parentbookmark = psdoc->bookmarkdict[parent-1];
6364 		bl = parentbookmark->children;
6365 	}
6366 	bookmark = (PS_BOOKMARK *) dlst_newnode(bl, sizeof(PS_BOOKMARK));
6367 	if(NULL == bookmark)  {
6368 		ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for new bookmark."));
6369 		return(0);
6370 	}
6371 	bookmark->page = psdoc->page;
6372 	bookmark->text = ps_strdup(psdoc, text);
6373 	bookmark->open = open;
6374 	if(psdoc->bookmarkcnt <= psdoc->lastbookmarkid) {
6375 		psdoc->bookmarkcnt += 20;
6376 		if(NULL == (psdoc->bookmarkdict = psdoc->realloc(psdoc, psdoc->bookmarkdict, psdoc->bookmarkcnt*sizeof(PS_BOOKMARK *), _("Allocate memory for new bookmark lookup table.")))) {
6377 			ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for larger bookmark lookup table."));
6378 			psdoc->bookmarkcnt -= 20;
6379 			psdoc->free(psdoc, bookmark->text);
6380 			dlst_freenode(bl, bookmark);
6381 			return(0);
6382 		}
6383 	}
6384 	psdoc->bookmarkdict[psdoc->lastbookmarkid] = bookmark;
6385 	if(NULL == (bookmark->children = dlst_init(psdoc->malloc, psdoc->realloc, psdoc->free))) {
6386 		ps_error(psdoc, PS_RuntimeError, _("Could not initialize bookmark list of new bookmark."));
6387 		psdoc->free(psdoc, bookmark->text);
6388 		dlst_freenode(bl, bookmark);
6389 		return(0);
6390 	}
6391 	psdoc->lastbookmarkid++;
6392 	bookmark->id = psdoc->lastbookmarkid;
6393 	dlst_insertafter(bl, bookmark, PS_DLST_HEAD(bl));
6394 	return(bookmark->id);
6395 }
6396 /* }}} */
6397 
6398 /* PS_add_note() {{{
6399  * Adds a note
6400  */
6401 PSLIB_API void PSLIB_CALL
6402 PS_add_note(PSDoc *psdoc, float llx, float lly, float urx, float ury, const char *contents, const char *title, const char *icon, int open) {
6403 	if(NULL == psdoc) {
6404 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6405 		return;
6406 	}
6407 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE)) {
6408 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page' scope."), __FUNCTION__);
6409 		return;
6410 	}
6411 
6412 	ps_printf(psdoc, "[ /Rect [ %f %f %f %f] ", llx, lly, urx, ury);
6413 	_ps_output_anno_border(psdoc);
6414 	if(open)
6415 		ps_printf(psdoc, "/Open true ");
6416 	if(0 == strcmp(icon, "comment"))
6417 		ps_printf(psdoc, "/Name /Comment ");
6418 	else if(0 == strcmp(icon, "insert"))
6419 		ps_printf(psdoc, "/Name /Insert ");
6420 	else if(0 == strcmp(icon, "note"))
6421 		ps_printf(psdoc, "/Name /Note ");
6422 	else if(0 == strcmp(icon, "paragraph"))
6423 		ps_printf(psdoc, "/Name /Paragraph ");
6424 	else if(0 == strcmp(icon, "newparagraph"))
6425 		ps_printf(psdoc, "/Name /Newparagraph ");
6426 	else if(0 == strcmp(icon, "key"))
6427 		ps_printf(psdoc, "/Name /Key ");
6428 	else if(0 == strcmp(icon, "help"))
6429 		ps_printf(psdoc, "/Name /Help ");
6430 	ps_printf(psdoc, "/Title (%s) /Contents (%s) /ANN pdfmark\n", title,  contents);
6431 }
6432 /* }}} */
6433 
6434 /* PS_hyphenate() {{{
6435  * hyphenate a word
6436  */
6437 PSLIB_API int PSLIB_CALL
6438 PS_hyphenate(PSDoc *psdoc, const char *text, char **hyphens) {
6439 	char *hyphentext;
6440 	int hyphenminchars;
6441 
6442 	*hyphens[0] = '\0';
6443 	if(!psdoc->hdict) {
6444 		ps_error(psdoc, PS_Warning, _("No hyphenation table set."));
6445 		return -1;
6446 	} else {
6447 		if(0 == (hyphenminchars = (int) PS_get_value(psdoc, "hyphenminchars", 0)))
6448 			hyphenminchars = 3;
6449 	}
6450 
6451 	hyphentext = ps_strdup(psdoc, text);
6452 	if(NULL != hyphentext) {
6453 		int k;
6454 		k = 0;
6455 		while(hyphentext[k] && !isalpha(hyphentext[k]))
6456 			k++;
6457 		if(strlen(hyphentext)-k > 2*hyphenminchars) {
6458 			char *buffer;
6459 			buffer = (char*) psdoc->malloc(psdoc, sizeof(char) * (strlen(hyphentext)+3), _("Could not allocate memory for hyphenation buffer."));
6460 			hnj_hyphen_hyphenate(psdoc->hdict, &hyphentext[k], strlen(&hyphentext[k]), buffer);
6461 			memset(*hyphens, '0', k);
6462 			memcpy(*hyphens+k, buffer, strlen(hyphentext)+1);
6463 			psdoc->free(psdoc, buffer);
6464 		} else {
6465 			return -1;
6466 		}
6467 		psdoc->free(psdoc, hyphentext);
6468 		return 0;
6469 	} else {
6470 		return -1;
6471 	}
6472 }
6473 /* }}} */
6474 
6475 /* Extra functions */
6476 
6477 /* PS_symbol() {{{
6478  * Output symbol without taking input encoding into account
6479  */
6480 PSLIB_API void PSLIB_CALL
6481 PS_symbol(PSDoc *psdoc, unsigned char c) {
6482 	char text[2];
6483 	ENCODING *fontenc;
6484 	ADOBEINFO *ai;
6485 
6486 	if(NULL == psdoc) {
6487 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6488 		return;
6489 	}
6490 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE)) {
6491 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page' scope."), __FUNCTION__);
6492 		return;
6493 	}
6494 	fontenc = ps_build_enc_vector(psdoc, psdoc->font->metrics->fontenc);
6495 	if(NULL == fontenc) {
6496 		ps_error(psdoc, PS_RuntimeError, _("Could not build font encoding vector."));
6497 		return;
6498 	}
6499 	ai = gfindadobe(psdoc->font->metrics->gadobechars, fontenc->vec[c]);
6500 	if(ai) {
6501 		text[0] = c;
6502 		text[1] = '\0';
6503 		ps_printf(psdoc, "%.2f %.2f a\n", psdoc->tstates[psdoc->tstate].tx, psdoc->tstates[psdoc->tstate].ty);
6504 		ps_render_text(psdoc, text);
6505 		psdoc->tstates[psdoc->tstate].tx += ai->width*psdoc->font->size/1000.0;
6506 	}
6507 	ps_free_enc_vector(psdoc, fontenc);
6508 
6509 }
6510 /* }}} */
6511 
6512 /* PS_glyph_show() {{{
6513  * Output symbol by its name
6514  */
6515 PSLIB_API void PSLIB_CALL
6516 PS_glyph_show(PSDoc *psdoc, const char *name) {
6517 	ADOBEINFO *ai;
6518 
6519 	if(NULL == psdoc) {
6520 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6521 		return;
6522 	}
6523 	if(!ps_check_scope(psdoc, PS_SCOPE_PAGE)) {
6524 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'page' scope."), __FUNCTION__);
6525 		return;
6526 	}
6527 	ai = gfindadobe(psdoc->font->metrics->gadobechars, name);
6528 	if(ai) {
6529 		ps_printf(psdoc, "%.2f %.2f a\n", psdoc->tstates[psdoc->tstate].tx, psdoc->tstates[psdoc->tstate].ty);
6530 		ps_printf(psdoc, "/%s glyphshow\n", name);
6531 		psdoc->tstates[psdoc->tstate].tx += ai->width*psdoc->font->size/1000.0;
6532 	} else {
6533 		ps_error(psdoc, PS_RuntimeError, _("glyph '%s' is not available in current font."), __FUNCTION__);
6534 	}
6535 }
6536 /* }}} */
6537 
6538 /* PS_glyph_width() {{{
6539  * Return width of a glyph
6540  */
6541 PSLIB_API float PSLIB_CALL
6542 PS_glyph_width(PSDoc *psdoc, const char *glyphname, int fontid, float size) {
6543 	ADOBEINFO *ai;
6544 	PSFont *psfont;
6545 
6546 	if(NULL == psdoc) {
6547 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6548 		return(0.0);
6549 	}
6550 
6551 	if(0 == fontid)
6552 		psfont = psdoc->font;
6553 	else {
6554 		if(NULL == (psfont = _ps_get_font(psdoc, fontid)))
6555 			return(0.0);
6556 	}
6557 
6558 	if(NULL == psfont) {
6559 		ps_error(psdoc, PS_RuntimeError, _("No font available."));
6560 		return(0.0);
6561 	}
6562 
6563 	if(NULL == psfont->metrics) {
6564 		ps_error(psdoc, PS_RuntimeError, _("No font metrics available. Cannot calculate width of string."));
6565 		return(0.0);
6566 	}
6567 
6568 	if(0.0 == size)
6569 		size = psfont->size;
6570 
6571 //	fprintf(stderr, "search for %s\n", glyphname);
6572 	ai = gfindadobe(psfont->metrics->gadobechars, glyphname);
6573 	if(ai) {
6574 //		fprintf(stderr, "width = %d, size = %f\n", ai->width, size);
6575 		return(ai->width*size/1000.0);
6576 	} else {
6577 		return(0.0);
6578 	}
6579 }
6580 /* }}} */
6581 
6582 /* PS_glyph_list() {{{
6583  * Returns list of all glyph names in the font. The list of glyphs must
6584  * be freed with PS_free_glyph_list().
6585  */
6586 PSLIB_API char** PSLIB_CALL
6587 PS_glyph_list(PSDoc *psdoc, int fontid, char ***charlist, int *len) {
6588 	ght_iterator_t iterator;
6589 	char *p_key;
6590 	ADOBEINFO *p_e;
6591 	PSFont *psfont;
6592 	int i;
6593 	char **tmp;
6594 
6595 	if(NULL == psdoc) {
6596 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6597 		return NULL;
6598 	}
6599 
6600 	if(0 == fontid)
6601 		psfont = psdoc->font;
6602 	else {
6603 		if(NULL == (psfont = _ps_get_font(psdoc, fontid)))
6604 			return NULL;
6605 	}
6606 
6607 	if(NULL == psfont) {
6608 		ps_error(psdoc, PS_RuntimeError, _("No font available."));
6609 		return NULL;
6610 	}
6611 
6612 	if(psfont->metrics->gadobechars) {
6613 		*len = ght_size(psfont->metrics->gadobechars);
6614 		if(NULL == (tmp = psdoc->malloc(psdoc, *len * sizeof(char **), _("Allocate memory for list of glyph names.")))) {
6615 		ps_error(psdoc, PS_RuntimeError, _("Could not allocate memory for list of glyph names."));
6616 			return NULL;
6617 		}
6618 		i = 0;
6619 		for(p_e = ght_first(psfont->metrics->gadobechars, &iterator, (void **) &p_key); p_e; p_e = ght_next(psfont->metrics->gadobechars, &iterator, (void **) &p_key)) {
6620 			tmp[i++] = ps_strdup(psdoc, p_e->adobename);
6621 		}
6622 		*charlist = tmp;
6623 		return tmp;
6624 	} else {
6625 		ps_error(psdoc, PS_RuntimeError, _("Font does not have list of glyphs."));
6626 		return NULL;
6627 	}
6628 }
6629 /* }}} */
6630 
6631 /* PS_free_glyph_list() {{{
6632  * Frees the memory allocated for a glyph list returned by PS_glyph_list().
6633  */
6634 PSLIB_API void PSLIB_CALL
6635 PS_free_glyph_list(PSDoc *psdoc, char **charlist, int len) {
6636 	int i;
6637 
6638 	if(NULL == psdoc) {
6639 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6640 		return;
6641 	}
6642 
6643 	for(i=0; i<len; i++) {
6644 		if(charlist[i])
6645 			psdoc->free(psdoc, charlist[i]);
6646 	}
6647 	psdoc->free(psdoc, charlist);
6648 }
6649 /* }}} */
6650 
6651 /* PS_symbol_width() {{{
6652  * Return width of a symbol without taking input encoding into account
6653  */
6654 PSLIB_API float PSLIB_CALL
6655 PS_symbol_width(PSDoc *psdoc, unsigned char c, int fontid, float size) {
6656 	ENCODING *fontenc;
6657 	ADOBEINFO *ai;
6658 	PSFont *psfont;
6659 
6660 	if(NULL == psdoc) {
6661 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6662 		return(0.0);
6663 	}
6664 
6665 	if(0 == fontid)
6666 		psfont = psdoc->font;
6667 	else {
6668 		if(NULL == (psfont = _ps_get_font(psdoc, fontid)))
6669 			return(0.0);
6670 	}
6671 
6672 	if(NULL == psfont) {
6673 		ps_error(psdoc, PS_RuntimeError, _("No font available."));
6674 		return(0.0);
6675 	}
6676 
6677 	if(NULL == psfont->metrics) {
6678 		ps_error(psdoc, PS_RuntimeError, _("No font metrics available. Cannot calculate width of string."));
6679 		return(0.0);
6680 	}
6681 
6682 	if(0.0 == size)
6683 		size = psfont->size;
6684 
6685 	fontenc = ps_build_enc_vector(psdoc, psfont->metrics->fontenc);
6686 	if(NULL == fontenc) {
6687 		ps_error(psdoc, PS_RuntimeError, _("Could not build font encoding vector."));
6688 		return(0.0);
6689 	}
6690 	ai = gfindadobe(psfont->metrics->gadobechars, fontenc->vec[c]);
6691 	ps_free_enc_vector(psdoc, fontenc);
6692 	if(ai) {
6693 		return(ai->width*size/1000.0);
6694 	} else {
6695 		return(0.0);
6696 	}
6697 }
6698 /* }}} */
6699 
6700 /* PS_symbol_name() {{{
6701  * Returns name of symbol
6702  */
6703 PSLIB_API void PSLIB_CALL
6704 PS_symbol_name(PSDoc *psdoc, unsigned char c, int fontid, char *name, int size) {
6705 	ENCODING *fontenc;
6706 	PSFont *psfont;
6707 
6708 	if(NULL == psdoc) {
6709 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6710 		return;
6711 	}
6712 
6713 	if(0 == fontid)
6714 		psfont = psdoc->font;
6715 	else {
6716 		if(NULL == (psfont = _ps_get_font(psdoc, fontid)))
6717 			return;
6718 	}
6719 
6720 	if(NULL == psfont) {
6721 		ps_error(psdoc, PS_RuntimeError, _("No font available."));
6722 		return;
6723 	}
6724 
6725 	if(NULL == psfont->metrics) {
6726 		ps_error(psdoc, PS_RuntimeError, _("No font metrics available. Cannot lookup symbol name."));
6727 		return;
6728 	}
6729 
6730 	fontenc = ps_build_enc_vector(psdoc, psfont->metrics->fontenc);
6731 	if(fontenc) {
6732 		if(fontenc->vec[c]) {
6733 			strncpy(name, fontenc->vec[c], size);
6734 		} else {
6735 			name[0] = '\0';
6736 		}
6737 		ps_free_enc_vector(psdoc, fontenc);
6738 	} else {
6739 		name[0] = '\0';
6740 	}
6741 	return;
6742 
6743 }
6744 /* }}} */
6745 
6746 /* PS_include_file() {{{
6747  * Includes a file into the output file
6748  */
6749 PSLIB_API int PSLIB_CALL
6750 PS_include_file(PSDoc *psdoc, const char *filename) {
6751 	unsigned char *bb;
6752 	FILE *fp;
6753 	long fsize;
6754 
6755 	if(NULL == psdoc) {
6756 		ps_error(psdoc, PS_RuntimeError, _("PSDoc is null."));
6757 		return -1;
6758 	}
6759 	/* If the header is not written, because we are before
6760 	 * the first page, then output the header first.
6761 	 */
6762 	if(psdoc->beginprologwritten == ps_false) {
6763 		ps_write_ps_comments(psdoc);
6764 		ps_write_ps_beginprolog(psdoc);
6765 	}
6766 	if(!ps_check_scope(psdoc, PS_SCOPE_PROLOG)) {
6767 		ps_error(psdoc, PS_RuntimeError, _("%s must be called within 'prolog' scope."), __FUNCTION__);
6768 		return -1;
6769 	}
6770 
6771 	if(NULL == filename || filename[0] == '\0') {
6772 		ps_error(psdoc, PS_IOError, _("Cannot include file without a name."));
6773 		return -1;
6774 	}
6775 
6776 	if(NULL == (fp = ps_open_file_in_path(psdoc, filename))) {
6777 		ps_error(psdoc, PS_IOError, _("Could not open include file '%s'."), filename);
6778 		return -1;
6779 	}
6780 
6781 	fseek(fp, 0, SEEK_END);
6782 	fsize = ftell(fp);
6783 	if(fsize <= 0) {
6784 		ps_error(psdoc, PS_Warning, _("Include file '%s' is empty"), filename);
6785 		fclose(fp);
6786 		return 0;
6787 	}
6788 	fseek(fp, 0, SEEK_SET);
6789 	if(NULL != (bb = malloc(fsize))) {
6790 		fread(bb, fsize, 1, fp);
6791 		ps_printf(psdoc, "PslibDict begin\n");
6792 		ps_write(psdoc, bb, fsize);
6793 		ps_printf(psdoc, "end\n");
6794 		free(bb);
6795 	} else {
6796 		ps_error(psdoc, PS_MemoryError, _("Could not allocate memory for include file '%s'"), filename);
6797 		return -1;
6798 	}
6799 	fclose(fp);
6800 	return 0;
6801 }
6802 /* }}} */
6803 
6804 /*
6805  * Local variables:
6806  * tab-width: 4
6807  * c-basic-offset: 4
6808  * End:
6809  * vim600: sw=2 ts=2 fdm=marker
6810  * vim<600: sw=2 ts=2
6811  */
6812