1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /*
3  * Copyright © 2004,2006 Red Hat, Inc.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software
6  * and its documentation for any purpose is hereby granted without
7  * fee, provided that the above copyright notice appear in all copies
8  * and that both that copyright notice and this permission notice
9  * appear in supporting documentation, and that the name of
10  * Red Hat, Inc. not be used in advertising or publicity pertaining to
11  * distribution of the software without specific, written prior
12  * permission. Red Hat, Inc. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as
14  * is" without express or implied warranty.
15  *
16  * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18  * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
19  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
20  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
22  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Author: Carl D. Worth <cworth@cworth.org>
25  */
26 
27 #include "cairo-boilerplate-private.h"
28 #include "cairo-boilerplate-scaled-font.h"
29 
30 #include <pixman.h>
31 
32 #include <cairo-types-private.h>
33 #include <cairo-scaled-font-private.h>
34 
35 #if CAIRO_HAS_SCRIPT_SURFACE
36 #include <cairo-script.h>
37 #endif
38 
39 #include <stddef.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <assert.h>
43 #include <errno.h>
44 
45 #if HAVE_DLFCN_H
46 #include <dlfcn.h>
47 #endif
48 
49 #if HAVE_UNISTD_H && HAVE_FCNTL_H && HAVE_SIGNAL_H && HAVE_SYS_STAT_H && HAVE_SYS_SOCKET_H && HAVE_SYS_UN_H
50 #include <unistd.h>
51 #include <fcntl.h>
52 #include <signal.h>
53 #include <sys/stat.h>
54 #include <sys/socket.h>
55 #include <sys/un.h>
56 
57 #define HAS_DAEMON 1
58 #define SOCKET_PATH "./.any2ppm"
59 #endif
60 
61 cairo_content_t
cairo_boilerplate_content(cairo_content_t content)62 cairo_boilerplate_content (cairo_content_t content)
63 {
64     if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
65 	content = CAIRO_CONTENT_COLOR_ALPHA;
66 
67     return content;
68 }
69 
70 const char *
cairo_boilerplate_content_name(cairo_content_t content)71 cairo_boilerplate_content_name (cairo_content_t content)
72 {
73     /* For the purpose of the content name, we don't distinguish the
74      * flattened content value.
75      */
76     switch (cairo_boilerplate_content (content)) {
77     case CAIRO_CONTENT_COLOR:
78 	return "rgb24";
79     case CAIRO_CONTENT_COLOR_ALPHA:
80 	return "argb32";
81     case CAIRO_CONTENT_ALPHA:
82     default:
83 	assert (0); /* not reached */
84 	return "---";
85     }
86 }
87 
88 static const char *
_cairo_boilerplate_content_visible_name(cairo_content_t content)89 _cairo_boilerplate_content_visible_name (cairo_content_t content)
90 {
91     switch (cairo_boilerplate_content (content)) {
92     case CAIRO_CONTENT_COLOR:
93 	return "rgb";
94     case CAIRO_CONTENT_COLOR_ALPHA:
95 	return "rgba";
96     case CAIRO_CONTENT_ALPHA:
97 	return "a";
98     default:
99 	assert (0); /* not reached */
100 	return "---";
101     }
102 }
103 
104 cairo_format_t
cairo_boilerplate_format_from_content(cairo_content_t content)105 cairo_boilerplate_format_from_content (cairo_content_t content)
106 {
107     cairo_format_t format;
108 
109     switch (content) {
110     case CAIRO_CONTENT_COLOR:
111         format = CAIRO_FORMAT_RGB24;
112         break;
113     case CAIRO_CONTENT_COLOR_ALPHA:
114         format = CAIRO_FORMAT_ARGB32;
115         break;
116     case CAIRO_CONTENT_ALPHA:
117         format = CAIRO_FORMAT_A8;
118         break;
119     default:
120         assert (0); /* not reached */
121         format = CAIRO_FORMAT_INVALID;
122         break;
123     }
124 
125     return format;
126 }
127 
128 static cairo_surface_t *
_cairo_boilerplate_image_create_surface(const char * name,cairo_content_t content,double width,double height,double max_width,double max_height,cairo_boilerplate_mode_t mode,void ** closure)129 _cairo_boilerplate_image_create_surface (const char		   *name,
130 					 cairo_content_t	    content,
131 					 double 		    width,
132 					 double 		    height,
133 					 double 		    max_width,
134 					 double 		    max_height,
135 					 cairo_boilerplate_mode_t   mode,
136 					 void			  **closure)
137 {
138     cairo_format_t format;
139 
140     *closure = NULL;
141 
142     if (content == CAIRO_CONTENT_COLOR_ALPHA) {
143 	format = CAIRO_FORMAT_ARGB32;
144     } else if (content == CAIRO_CONTENT_COLOR) {
145 	format = CAIRO_FORMAT_RGB24;
146     } else {
147 	assert (0); /* not reached */
148 	return NULL;
149     }
150 
151     return cairo_image_surface_create (format, ceil (width), ceil (height));
152 }
153 
154 static const cairo_user_data_key_t key;
155 
156 static cairo_surface_t *
_cairo_boilerplate_image_create_similar(cairo_surface_t * other,cairo_content_t content,int width,int height)157 _cairo_boilerplate_image_create_similar (cairo_surface_t *other,
158 					 cairo_content_t content,
159 					 int width, int height)
160 {
161     cairo_format_t format;
162     cairo_surface_t *surface;
163     int stride;
164     void *ptr;
165 
166     switch (content) {
167     case CAIRO_CONTENT_ALPHA:
168         format = CAIRO_FORMAT_A8;
169         break;
170     case CAIRO_CONTENT_COLOR:
171         format = CAIRO_FORMAT_RGB24;
172         break;
173     case CAIRO_CONTENT_COLOR_ALPHA:
174     default:
175         format = CAIRO_FORMAT_ARGB32;
176         break;
177     }
178 
179     stride = cairo_format_stride_for_width(format, width);
180     ptr = malloc (stride* height);
181 
182     surface = cairo_image_surface_create_for_data (ptr, format,
183 						   width, height, stride);
184     cairo_surface_set_user_data (surface, &key, ptr, free);
185 
186     return surface;
187 }
188 
189 static cairo_surface_t *
_cairo_boilerplate_image16_create_surface(const char * name,cairo_content_t content,double width,double height,double max_width,double max_height,cairo_boilerplate_mode_t mode,void ** closure)190 _cairo_boilerplate_image16_create_surface (const char		     *name,
191 					   cairo_content_t	      content,
192 					   double		      width,
193 					   double		      height,
194 					   double		      max_width,
195 					   double		      max_height,
196 					   cairo_boilerplate_mode_t   mode,
197 					   void			    **closure)
198 {
199     *closure = NULL;
200 
201     /* XXX force CAIRO_CONTENT_COLOR */
202     return cairo_image_surface_create (CAIRO_FORMAT_RGB16_565, ceil (width), ceil (height));
203 }
204 
205 static cairo_surface_t *
_cairo_boilerplate_image16_create_similar(cairo_surface_t * other,cairo_content_t content,int width,int height)206 _cairo_boilerplate_image16_create_similar (cairo_surface_t *other,
207 					   cairo_content_t content,
208 					   int width, int height)
209 {
210     cairo_format_t format;
211     cairo_surface_t *surface;
212     int stride;
213     void *ptr;
214 
215     switch (content) {
216     case CAIRO_CONTENT_ALPHA:
217         format = CAIRO_FORMAT_A8;
218         break;
219     case CAIRO_CONTENT_COLOR:
220         format = CAIRO_FORMAT_RGB16_565;
221         break;
222     case CAIRO_CONTENT_COLOR_ALPHA:
223     default:
224         format = CAIRO_FORMAT_ARGB32;
225         break;
226     }
227 
228     stride = cairo_format_stride_for_width(format, width);
229     ptr = malloc (stride* height);
230 
231     surface = cairo_image_surface_create_for_data (ptr, format,
232 						   width, height, stride);
233     cairo_surface_set_user_data (surface, &key, ptr, free);
234 
235     return surface;
236 }
237 
238 static char *
_cairo_boilerplate_image_describe(void * closure)239 _cairo_boilerplate_image_describe (void *closure)
240 {
241     char *s;
242 
243     xasprintf (&s, "pixman %s", pixman_version_string ());
244 
245     return s;
246 }
247 
248 #if CAIRO_HAS_RECORDING_SURFACE
249 static cairo_surface_t *
_cairo_boilerplate_recording_create_surface(const char * name,cairo_content_t content,double width,double height,double max_width,double max_height,cairo_boilerplate_mode_t mode,void ** closure)250 _cairo_boilerplate_recording_create_surface (const char 	       *name,
251 					     cairo_content_t		content,
252 					     double			width,
253 					     double			height,
254 					     double			max_width,
255 					     double			max_height,
256 					     cairo_boilerplate_mode_t	mode,
257 					     void		      **closure)
258 {
259     cairo_rectangle_t extents;
260 
261     extents.x = 0;
262     extents.y = 0;
263     extents.width = width;
264     extents.height = height;
265     return *closure = cairo_surface_reference (cairo_recording_surface_create (content, &extents));
266 }
267 
268 static void
_cairo_boilerplate_recording_surface_cleanup(void * closure)269 _cairo_boilerplate_recording_surface_cleanup (void *closure)
270 {
271     cairo_surface_finish (closure);
272     cairo_surface_destroy (closure);
273 }
274 #endif
275 
276 const cairo_user_data_key_t cairo_boilerplate_output_basename_key;
277 
278 cairo_surface_t *
_cairo_boilerplate_get_image_surface(cairo_surface_t * src,int page,int width,int height)279 _cairo_boilerplate_get_image_surface (cairo_surface_t *src,
280 				      int	       page,
281 				      int	       width,
282 				      int	       height)
283 {
284     cairo_surface_t *surface, *image;
285     cairo_t *cr;
286     cairo_status_t status;
287     cairo_format_t format;
288 
289     if (cairo_surface_status (src))
290 	return cairo_surface_reference (src);
291 
292     if (page != 0)
293 	return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
294 
295     /* extract sub-surface */
296     switch (cairo_surface_get_content (src)) {
297     case CAIRO_CONTENT_ALPHA:
298 	format = CAIRO_FORMAT_A8;
299 	break;
300     case CAIRO_CONTENT_COLOR:
301 	format = CAIRO_FORMAT_RGB24;
302 	break;
303     default:
304     case CAIRO_CONTENT_COLOR_ALPHA:
305 	format = CAIRO_FORMAT_ARGB32;
306 	break;
307     }
308     surface = cairo_image_surface_create (format, width, height);
309     assert (cairo_surface_get_content (surface) == cairo_surface_get_content (src));
310     image = cairo_surface_reference (surface);
311 
312     /* open a logging channel (only interesting for recording surfaces) */
313 #if CAIRO_HAS_SCRIPT_SURFACE && CAIRO_HAS_RECORDING_SURFACE
314     if (cairo_surface_get_type (src) == CAIRO_SURFACE_TYPE_RECORDING) {
315 	const char *test_name;
316 
317 	test_name = cairo_surface_get_user_data (src,
318 						 &cairo_boilerplate_output_basename_key);
319 	if (test_name != NULL) {
320 	    cairo_device_t *ctx;
321 	    char *filename;
322 
323 	    cairo_surface_destroy (surface);
324 
325 	    xasprintf (&filename, "%s.out.trace", test_name);
326 	    ctx = cairo_script_create (filename);
327 	    surface = cairo_script_surface_create_for_target (ctx, image);
328 	    cairo_device_destroy (ctx);
329 	    free (filename);
330 	}
331     }
332 #endif
333 
334     cr = cairo_create (surface);
335     cairo_surface_destroy (surface);
336     cairo_set_source_surface (cr, src, 0, 0);
337     cairo_paint (cr);
338 
339     status = cairo_status (cr);
340     if (status) {
341 	cairo_surface_destroy (image);
342 	image = cairo_surface_reference (cairo_get_target (cr));
343     }
344     cairo_destroy (cr);
345 
346     return image;
347 }
348 
349 cairo_surface_t *
cairo_boilerplate_get_image_surface_from_png(const char * filename,int width,int height,cairo_bool_t flatten)350 cairo_boilerplate_get_image_surface_from_png (const char   *filename,
351 					      int	    width,
352 					      int	    height,
353 					      cairo_bool_t  flatten)
354 {
355     cairo_surface_t *surface;
356 
357     surface = cairo_image_surface_create_from_png (filename);
358     if (cairo_surface_status (surface))
359 	return surface;
360 
361     if (flatten) {
362 	cairo_t *cr;
363 	cairo_surface_t *flattened;
364 
365 	flattened = cairo_image_surface_create (cairo_image_surface_get_format (surface),
366 						width,
367 						height);
368 	cr = cairo_create (flattened);
369 	cairo_surface_destroy (flattened);
370 
371 	cairo_set_source_rgb (cr, 1, 1, 1);
372 	cairo_paint (cr);
373 
374 	cairo_set_source_surface (cr, surface,
375 				  width - cairo_image_surface_get_width (surface),
376 				  height - cairo_image_surface_get_height (surface));
377 	cairo_paint (cr);
378 
379 	cairo_surface_destroy (surface);
380 	surface = cairo_surface_reference (cairo_get_target (cr));
381 	cairo_destroy (cr);
382     } else if (cairo_image_surface_get_width (surface) != width ||
383 	       cairo_image_surface_get_height (surface) != height)
384     {
385 	cairo_t *cr;
386 	cairo_surface_t *sub;
387 
388 	sub = cairo_image_surface_create (cairo_image_surface_get_format (surface),
389 					  width,
390 					  height);
391 	cr = cairo_create (sub);
392 	cairo_surface_destroy (sub);
393 
394 	cairo_set_source_surface (cr, surface,
395 				  width - cairo_image_surface_get_width (surface),
396 				  height - cairo_image_surface_get_height (surface));
397 	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
398 	cairo_paint (cr);
399 
400 	cairo_surface_destroy (surface);
401 	surface = cairo_surface_reference (cairo_get_target (cr));
402 	cairo_destroy (cr);
403     }
404 
405     return surface;
406 }
407 
408 static const cairo_boilerplate_target_t builtin_targets[] = {
409     /* I'm uncompromising about leaving the image backend as 0
410      * for tolerance. There shouldn't ever be anything that is out of
411      * our control here. */
412     {
413 	"image", "image", NULL, NULL,
414 	CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR_ALPHA, 0,
415 	NULL,
416 	_cairo_boilerplate_image_create_surface,
417 	_cairo_boilerplate_image_create_similar,
418 	NULL, NULL,
419 	_cairo_boilerplate_get_image_surface,
420 	cairo_surface_write_to_png,
421 	NULL, NULL,
422         _cairo_boilerplate_image_describe,
423 	TRUE, FALSE, FALSE
424     },
425     {
426 	"image", "image", NULL, NULL,
427 	CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0,
428 	NULL,
429 	_cairo_boilerplate_image_create_surface,
430 	_cairo_boilerplate_image_create_similar,
431 	NULL, NULL,
432 	_cairo_boilerplate_get_image_surface,
433 	cairo_surface_write_to_png,
434 	NULL, NULL,
435         _cairo_boilerplate_image_describe,
436 	FALSE, FALSE, FALSE
437     },
438     {
439 	"image16", "image", NULL, NULL,
440 	CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0,
441 	NULL,
442 	_cairo_boilerplate_image16_create_surface,
443 	_cairo_boilerplate_image16_create_similar,
444 	NULL, NULL,
445 	_cairo_boilerplate_get_image_surface,
446 	cairo_surface_write_to_png,
447 	NULL, NULL,
448         _cairo_boilerplate_image_describe,
449 	TRUE, FALSE, FALSE
450     },
451 #if CAIRO_HAS_RECORDING_SURFACE
452     {
453 	"recording", "image", NULL, NULL,
454 	CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR_ALPHA, 0,
455 	"cairo_recording_surface_create",
456 	_cairo_boilerplate_recording_create_surface,
457 	cairo_surface_create_similar,
458 	NULL, NULL,
459 	_cairo_boilerplate_get_image_surface,
460 	cairo_surface_write_to_png,
461 	_cairo_boilerplate_recording_surface_cleanup,
462 	NULL, NULL,
463 	FALSE, FALSE, TRUE
464     },
465     {
466 	"recording", "image", NULL, NULL,
467 	CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
468 	"cairo_recording_surface_create",
469 	_cairo_boilerplate_recording_create_surface,
470 	cairo_surface_create_similar,
471 	NULL, NULL,
472 	_cairo_boilerplate_get_image_surface,
473 	cairo_surface_write_to_png,
474 	_cairo_boilerplate_recording_surface_cleanup,
475 	NULL, NULL,
476 	FALSE, FALSE, TRUE
477     },
478 #endif
479 };
480 CAIRO_BOILERPLATE (builtin, builtin_targets)
481 
482 static struct cairo_boilerplate_target_list {
483     struct cairo_boilerplate_target_list *next;
484     const cairo_boilerplate_target_t *target;
485 } *cairo_boilerplate_targets;
486 
487 static cairo_bool_t
probe_target(const cairo_boilerplate_target_t * target)488 probe_target (const cairo_boilerplate_target_t *target)
489 {
490     if (target->probe == NULL)
491 	return TRUE;
492 
493 #if HAVE_DLSYM
494     return dlsym (NULL, target->probe) != NULL;
495 #else
496     return TRUE;
497 #endif
498 }
499 
500 void
_cairo_boilerplate_register_backend(const cairo_boilerplate_target_t * targets,unsigned int count)501 _cairo_boilerplate_register_backend (const cairo_boilerplate_target_t *targets,
502 				     unsigned int		       count)
503 {
504     targets += count;
505     while (count--) {
506 	struct cairo_boilerplate_target_list *list;
507 
508 	--targets;
509 	if (! probe_target (targets))
510 	    continue;
511 
512 	list = xmalloc (sizeof (*list));
513 	list->next = cairo_boilerplate_targets;
514 	list->target = targets;
515 	cairo_boilerplate_targets = list;
516     }
517 }
518 
519 static cairo_bool_t
_cairo_boilerplate_target_format_matches_name(const cairo_boilerplate_target_t * target,const char * tcontent_name,const char * tcontent_end)520 _cairo_boilerplate_target_format_matches_name (const cairo_boilerplate_target_t *target,
521 					const char *tcontent_name,
522 					const char *tcontent_end)
523 {
524 	char const *content_name;
525 	const char *content_end = tcontent_end;
526 	size_t content_len;
527 
528 	content_name = _cairo_boilerplate_content_visible_name (target->content);
529 	if (tcontent_end)
530 		content_len = content_end - tcontent_name;
531 	else
532 		content_len = strlen(tcontent_name);
533 	if (strlen(content_name) != content_len)
534 		return FALSE;
535 	if (0 == strncmp (content_name, tcontent_name, content_len))
536 		return TRUE;
537 
538 	return FALSE;
539 }
540 
541 static cairo_bool_t
_cairo_boilerplate_target_matches_name(const cairo_boilerplate_target_t * target,const char * tname,const char * end)542 _cairo_boilerplate_target_matches_name (const cairo_boilerplate_target_t *target,
543 					const char			 *tname,
544 					const char			 *end)
545 {
546     char const *content_name;
547     const char *content_start = strpbrk (tname, ".");
548     const char *content_end = end;
549     size_t name_len;
550     size_t content_len;
551 
552     if (content_start >= end)
553 	content_start = NULL;
554     if (content_start != NULL)
555 	end = content_start++;
556 
557     name_len = end - tname;
558 
559     /* Check name. */
560     if (! (name_len == 1 && 0 == strncmp (tname, "?", 1))) { /* wildcard? */
561 	if (0 != strncmp (target->name, tname, name_len)) /* exact match? */
562 	    return FALSE;
563 	if (isalnum (target->name[name_len]))
564 	    return FALSE;
565     }
566 
567     /* Check optional content. */
568     if (content_start == NULL)	/* none given? */
569 	return TRUE;
570 
571     /* Exact content match? */
572     content_name = _cairo_boilerplate_content_visible_name (target->content);
573     content_len = content_end - content_start;
574     if (strlen(content_name) != content_len)
575 	return FALSE;
576     if (0 == strncmp (content_name, content_start, content_len))
577 	return TRUE;
578 
579     return FALSE;
580 }
581 
582 const cairo_boilerplate_target_t **
cairo_boilerplate_get_targets(int * pnum_targets,cairo_bool_t * plimited_targets)583 cairo_boilerplate_get_targets (int	    *pnum_targets,
584 			       cairo_bool_t *plimited_targets)
585 {
586     size_t i, num_targets;
587     cairo_bool_t limited_targets = FALSE;
588     const char *tname;
589     const cairo_boilerplate_target_t **targets_to_test;
590     struct cairo_boilerplate_target_list *list;
591 
592     if (cairo_boilerplate_targets == NULL)
593 	_cairo_boilerplate_register_all ();
594 
595     if ((tname = getenv ("CAIRO_TEST_TARGET")) != NULL && *tname) {
596 	/* check the list of targets specified by the user */
597 	limited_targets = TRUE;
598 
599 	num_targets = 0;
600 	targets_to_test = NULL;
601 
602 	while (*tname) {
603 	    int found = 0;
604 	    const char *end = strpbrk (tname, " \t\r\n;:,");
605 	    if (!end)
606 		end = tname + strlen (tname);
607 
608 	    if (end == tname) {
609 		tname = end + 1;
610 		continue;
611 	    }
612 
613 	    for (list = cairo_boilerplate_targets;
614 		 list != NULL;
615 		 list = list->next)
616 	    {
617 		    const cairo_boilerplate_target_t *target = list->target;
618 		    const char *tcontent_name;
619 		    const char *tcontent_end;
620 		    if (_cairo_boilerplate_target_matches_name (target, tname, end)) {
621 			    if ((tcontent_name = getenv ("CAIRO_TEST_TARGET_FORMAT")) != NULL && *tcontent_name) {
622 				    while(tcontent_name) {
623 					    tcontent_end = strpbrk (tcontent_name, " \t\r\n;:,");
624 					    if (tcontent_end == tcontent_name) {
625 						    tcontent_name = tcontent_end + 1;
626 						    continue;
627 					    }
628 					    if(_cairo_boilerplate_target_format_matches_name (target,
629 								    tcontent_name, tcontent_end)) {
630 						    /* realloc isn't exactly the best thing here, but meh. */
631 						    targets_to_test = xrealloc (targets_to_test,
632 								    sizeof(cairo_boilerplate_target_t *) * (num_targets+1));
633 						    targets_to_test[num_targets++] = target;
634 						    found = 1;
635 					    }
636 
637 					    if (tcontent_end)
638 						    tcontent_end++;
639 					    tcontent_name = tcontent_end;
640 				    }
641 			    } else {
642 				    /* realloc isn't exactly the best thing here, but meh. */
643 				    targets_to_test = xrealloc (targets_to_test,
644 						    sizeof(cairo_boilerplate_target_t *) * (num_targets+1));
645 				    targets_to_test[num_targets++] = target;
646 				    found = 1;
647 			    }
648 		    }
649 	    }
650 
651 	    if (!found) {
652 		const char *last_name = NULL;
653 
654 		fprintf (stderr, "Cannot find target '%.*s'.\n",
655 			 (int)(end - tname), tname);
656 		fprintf (stderr, "Known targets:");
657 		for (list = cairo_boilerplate_targets;
658 		     list != NULL;
659 		     list = list->next)
660 		{
661 		    const cairo_boilerplate_target_t *target = list->target;
662 		    if (last_name != NULL) {
663 			if (strcmp (target->name, last_name) == 0) {
664 			    /* filter out repeats that differ in content */
665 			    continue;
666 			}
667 			fprintf (stderr, ",");
668 		    }
669 		    fprintf (stderr, " %s", target->name);
670 		    last_name = target->name;
671 		}
672 		fprintf (stderr, "\n");
673 		exit(-1);
674 	    }
675 
676 	    if (*end)
677 	      end++;
678 	    tname = end;
679 	}
680     } else {
681 	    int found = 0;
682 	    int not_found_targets = 0;
683 	    num_targets = 0;
684 	    targets_to_test = xmalloc (sizeof(cairo_boilerplate_target_t*) * num_targets);
685 	    for (list = cairo_boilerplate_targets; list != NULL; list = list->next)
686 	    {
687 		    const cairo_boilerplate_target_t *target = list->target;
688 		    const char *tcontent_name;
689 		    const char *tcontent_end;
690 		    if ((tcontent_name = getenv ("CAIRO_TEST_TARGET_FORMAT")) != NULL && *tcontent_name) {
691 			    while(tcontent_name) {
692 				    tcontent_end = strpbrk (tcontent_name, " \t\r\n;:,");
693 				    if (tcontent_end == tcontent_name) {
694 					    tcontent_name = tcontent_end + 1;
695 					    continue;
696 				    }
697 				    if (_cairo_boilerplate_target_format_matches_name (target,
698 							    tcontent_name, tcontent_end)) {
699 					    /* realloc isn't exactly the best thing here, but meh. */
700 					    targets_to_test = xrealloc (targets_to_test,
701 							    sizeof(cairo_boilerplate_target_t *) * (num_targets+1));
702 					    targets_to_test[num_targets++] = target;
703 					    found =1;
704 				    }
705 				    else
706 				    {
707 					    not_found_targets++;
708 				    }
709 
710 				    if (tcontent_end)
711 					    tcontent_end++;
712 
713 				    tcontent_name = tcontent_end;
714 			    }
715 		    }
716 		    else
717 		    {
718 			    num_targets++;
719 		    }
720 	    }
721 	    if (!found)
722 	    {
723 		    /* check all compiled in targets */
724 		    num_targets = num_targets + not_found_targets;
725 		    targets_to_test = xrealloc (targets_to_test,
726 				    sizeof(cairo_boilerplate_target_t*) * num_targets);
727 		    num_targets = 0;
728 		    for (list = cairo_boilerplate_targets;
729 				    list != NULL;
730 				    list = list->next)
731 		    {
732 			    const cairo_boilerplate_target_t *target = list->target;
733 			    targets_to_test[num_targets++] = target;
734 		    }
735 	    }
736 
737     }
738 
739     /* exclude targets as specified by the user */
740     if ((tname = getenv ("CAIRO_TEST_TARGET_EXCLUDE")) != NULL && *tname) {
741 	limited_targets = TRUE;
742 
743 	while (*tname) {
744 	    int j;
745 	    const char *end = strpbrk (tname, " \t\r\n;:,");
746 	    if (!end)
747 		end = tname + strlen (tname);
748 
749 	    if (end == tname) {
750 		tname = end + 1;
751 		continue;
752 	    }
753 
754 	    for (i = j = 0; i < num_targets; i++) {
755 		const cairo_boilerplate_target_t *target = targets_to_test[i];
756 		if (! _cairo_boilerplate_target_matches_name (target,
757 							      tname, end))
758 		{
759 		    targets_to_test[j++] = targets_to_test[i];
760 		}
761 	    }
762 	    num_targets = j;
763 
764 	    if (*end)
765 	      end++;
766 	    tname = end;
767 	}
768     }
769 
770     if (pnum_targets)
771 	*pnum_targets = num_targets;
772 
773     if (plimited_targets)
774 	*plimited_targets = limited_targets;
775 
776     return targets_to_test;
777 }
778 
779 const cairo_boilerplate_target_t *
cairo_boilerplate_get_image_target(cairo_content_t content)780 cairo_boilerplate_get_image_target (cairo_content_t content)
781 {
782     if (cairo_boilerplate_targets == NULL)
783 	_cairo_boilerplate_register_all ();
784 
785     switch (content) {
786     case CAIRO_CONTENT_COLOR:
787         return &builtin_targets[1];
788     case CAIRO_CONTENT_COLOR_ALPHA:
789         return &builtin_targets[0];
790     case CAIRO_CONTENT_ALPHA:
791     default:
792         return NULL;
793     }
794 }
795 
796 const cairo_boilerplate_target_t *
cairo_boilerplate_get_target_by_name(const char * name,cairo_content_t content)797 cairo_boilerplate_get_target_by_name (const char      *name,
798 				      cairo_content_t  content)
799 {
800     struct cairo_boilerplate_target_list *list;
801 
802     if (cairo_boilerplate_targets == NULL)
803 	_cairo_boilerplate_register_all ();
804 
805     /* first return an exact match */
806     for (list = cairo_boilerplate_targets; list != NULL; list = list->next) {
807 	const cairo_boilerplate_target_t *target = list->target;
808 	if (strcmp (target->name, name) == 0 &&
809 	    target->content == content)
810 	{
811 	    return target;
812 	}
813     }
814 
815     /* otherwise just return a match that may differ in content */
816     for (list = cairo_boilerplate_targets; list != NULL; list = list->next) {
817 	const cairo_boilerplate_target_t *target = list->target;
818 	if (strcmp (target->name, name) == 0)
819 	    return target;
820     }
821 
822     return NULL;
823 }
824 
825 void
cairo_boilerplate_free_targets(const cairo_boilerplate_target_t ** targets)826 cairo_boilerplate_free_targets (const cairo_boilerplate_target_t **targets)
827 {
828     free (targets);
829 }
830 
831 cairo_surface_t *
cairo_boilerplate_surface_create_in_error(cairo_status_t status)832 cairo_boilerplate_surface_create_in_error (cairo_status_t status)
833 {
834     cairo_surface_t *surface = NULL;
835     int loop = 5;
836 
837     do {
838 	cairo_surface_t *intermediate;
839 	cairo_t *cr;
840 	cairo_path_t path;
841 
842 	intermediate = cairo_image_surface_create (CAIRO_FORMAT_A8, 0, 0);
843 	cr = cairo_create (intermediate);
844 	cairo_surface_destroy (intermediate);
845 
846 	path.status = status;
847 	cairo_append_path (cr, &path);
848 
849 	cairo_surface_destroy (surface);
850 	surface = cairo_surface_reference (cairo_get_target (cr));
851 	cairo_destroy (cr);
852     } while (cairo_surface_status (surface) != status && --loop);
853 
854     return surface;
855 }
856 
857 void
cairo_boilerplate_scaled_font_set_max_glyphs_cached(cairo_scaled_font_t * scaled_font,int max_glyphs)858 cairo_boilerplate_scaled_font_set_max_glyphs_cached (cairo_scaled_font_t *scaled_font,
859 						     int		  max_glyphs)
860 {
861     /* XXX CAIRO_DEBUG */
862 }
863 
864 #if HAS_DAEMON
865 static int
any2ppm_daemon_exists(void)866 any2ppm_daemon_exists (void)
867 {
868     struct stat st;
869     int fd;
870     char buf[80];
871     int pid;
872     int ret;
873 
874     if (stat (SOCKET_PATH, &st) < 0)
875 	return 0;
876 
877     fd = open (SOCKET_PATH ".pid", O_RDONLY);
878     if (fd < 0)
879 	return 0;
880 
881     pid = 0;
882     ret = read (fd, buf, sizeof (buf) - 1);
883     if (ret > 0) {
884 	buf[ret] = '\0';
885 	pid = atoi (buf);
886     }
887     close (fd);
888 
889     return pid > 0 && kill (pid, 0) != -1;
890 }
891 #endif
892 
893 FILE *
cairo_boilerplate_open_any2ppm(const char * filename,int page,unsigned int flags,int (** close_cb)(FILE *))894 cairo_boilerplate_open_any2ppm (const char   *filename,
895 				int	      page,
896 				unsigned int  flags,
897 				int        (**close_cb) (FILE *))
898 {
899     char command[4096];
900     const char *any2ppm;
901 #if HAS_DAEMON
902     int sk;
903     struct sockaddr_un addr;
904     int len;
905 #endif
906 
907     any2ppm = getenv ("ANY2PPM");
908     if (any2ppm == NULL)
909 	any2ppm = "./any2ppm";
910 
911 #if HAS_DAEMON
912     if (flags & CAIRO_BOILERPLATE_OPEN_NO_DAEMON)
913 	goto POPEN;
914 
915     if (! any2ppm_daemon_exists ()) {
916 	if (system (any2ppm) != 0)
917 	    goto POPEN;
918     }
919 
920     sk = socket (PF_UNIX, SOCK_STREAM, 0);
921     if (sk == -1)
922 	goto POPEN;
923 
924     memset (&addr, 0, sizeof (addr));
925     addr.sun_family = AF_UNIX;
926     strcpy (addr.sun_path, SOCKET_PATH);
927 
928     if (connect (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
929 	close (sk);
930 	goto POPEN;
931     }
932 
933     len = sprintf (command, "%s %d\n", filename, page);
934     if (write (sk, command, len) != len) {
935 	close (sk);
936 	goto POPEN;
937     }
938 
939     *close_cb = fclose;
940     return fdopen (sk, "rb");
941 
942 POPEN:
943 #endif
944 
945     *close_cb = pclose;
946     sprintf (command, "%s %s %d", any2ppm, filename, page);
947     return popen (command, "rb");
948 }
949 
950 static cairo_bool_t
freadn(unsigned char * buf,int len,FILE * file)951 freadn (unsigned char *buf,
952 	int	       len,
953 	FILE	      *file)
954 {
955     int ret;
956 
957     while (len) {
958 	ret = fread (buf, 1, len, file);
959 	if (ret != len) {
960 	    if (ferror (file) || feof (file))
961 		return FALSE;
962 	}
963 	len -= ret;
964 	buf += len;
965     }
966 
967     return TRUE;
968 }
969 
970 cairo_surface_t *
cairo_boilerplate_image_surface_create_from_ppm_stream(FILE * file)971 cairo_boilerplate_image_surface_create_from_ppm_stream (FILE *file)
972 {
973     char format;
974     int width, height;
975     ptrdiff_t stride;
976     int x, y;
977     unsigned char *data;
978     cairo_surface_t *image = NULL;
979 
980     if (fscanf (file, "P%c %d %d 255\n", &format, &width, &height) != 3)
981 	goto FAIL;
982 
983     switch (format) {
984     case '7': /* XXX */
985 	image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
986 	break;
987     case '6':
988 	image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
989 	break;
990     case '5':
991 	image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
992 	break;
993     default:
994 	goto FAIL;
995     }
996     if (cairo_surface_status (image))
997 	return image;
998 
999     data = cairo_image_surface_get_data (image);
1000     stride = cairo_image_surface_get_stride (image);
1001     for (y = 0; y < height; y++) {
1002 	unsigned char *buf = data + y*stride;
1003 	switch (format) {
1004 	case '7':
1005 	    if (! freadn (buf, 4 * width, file))
1006 		goto FAIL;
1007 	    break;
1008 	case '6':
1009 	    if (! freadn (buf, 3*width, file))
1010 		goto FAIL;
1011 	    buf += 3*width;
1012 	    for (x = width; x--; ) {
1013 		buf -= 3;
1014 		((uint32_t *) (data + y*stride))[x] =
1015 		    (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0);
1016 	    }
1017 	    break;
1018 	case '5':
1019 	    if (! freadn (buf, width, file))
1020 		goto FAIL;
1021 	    break;
1022 	}
1023     }
1024     cairo_surface_mark_dirty (image);
1025 
1026     return image;
1027 
1028 FAIL:
1029     cairo_surface_destroy (image);
1030     return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_READ_ERROR);
1031 }
1032 
1033 cairo_surface_t *
cairo_boilerplate_convert_to_image(const char * filename,int page)1034 cairo_boilerplate_convert_to_image (const char *filename,
1035 				    int 	page)
1036 {
1037     FILE *file;
1038     unsigned int flags = 0;
1039     cairo_surface_t *image;
1040     int (*close_cb) (FILE *);
1041     int ret;
1042 
1043   RETRY:
1044     file = cairo_boilerplate_open_any2ppm (filename, page, flags, &close_cb);
1045     if (file == NULL) {
1046 	switch (errno) {
1047 	case ENOMEM:
1048 	    return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
1049 	default:
1050 	    return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_READ_ERROR);
1051 	}
1052     }
1053 
1054     image = cairo_boilerplate_image_surface_create_from_ppm_stream (file);
1055     ret = close_cb (file);
1056     /* check for fatal errors from the interpreter */
1057     if (ret) { /* any2pmm should never die... */
1058 	cairo_surface_destroy (image);
1059 	return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_INVALID_STATUS);
1060     }
1061 
1062     if (ret == 0 && cairo_surface_status (image) == CAIRO_STATUS_READ_ERROR) {
1063 	if (flags == 0) {
1064 	    /* Try again in a standalone process. */
1065 	    cairo_surface_destroy (image);
1066 	    flags = CAIRO_BOILERPLATE_OPEN_NO_DAEMON;
1067 	    goto RETRY;
1068 	}
1069     }
1070 
1071     return image;
1072 }
1073 
1074 int
cairo_boilerplate_version(void)1075 cairo_boilerplate_version (void)
1076 {
1077     return CAIRO_VERSION;
1078 }
1079 
1080 const char*
cairo_boilerplate_version_string(void)1081 cairo_boilerplate_version_string (void)
1082 {
1083     return CAIRO_VERSION_STRING;
1084 }
1085 
1086 void
cairo_boilerplate_fini(void)1087 cairo_boilerplate_fini (void)
1088 {
1089     while (cairo_boilerplate_targets != NULL) {
1090 	struct cairo_boilerplate_target_list *next;
1091 
1092 	next = cairo_boilerplate_targets->next;
1093 
1094 	free (cairo_boilerplate_targets);
1095 	cairo_boilerplate_targets = next;
1096     }
1097 }
1098