1
/*
2
 * Copyright © 2006, 2008 Red Hat, Inc.
3
 *
4
 * Permission to use, copy, modify, distribute, and sell this software
5
 * and its documentation for any purpose is hereby granted without
6
 * fee, provided that the above copyright notice appear in all copies
7
 * and that both that copyright notice and this permission notice
8
 * appear in supporting documentation, and that the name of
9
 * Red Hat, Inc. not be used in advertising or publicity pertaining to
10
 * distribution of the software without specific, written prior
11
 * permission. Red Hat, Inc. makes no representations about the
12
 * suitability of this software for any purpose.  It is provided "as
13
 * is" without express or implied warranty.
14
 *
15
 * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17
 * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
18
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
21
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
 *
23
 * Contributor(s):
24
 *	Kristian Høgsberg <krh@redhat.com>
25
 *	Behdad Esfahbod <behdad@behdad.org>
26
 */
27

            
28
#include "cairo-test.h"
29

            
30
#include <stdlib.h>
31
#include <stdio.h>
32

            
33
/*#define ROTATED 1*/
34

            
35
#define BORDER 10
36
#define TEXT_SIZE 64
37
#define WIDTH  (TEXT_SIZE * 16 + 2*BORDER)
38
#ifndef ROTATED
39
 #define HEIGHT ((TEXT_SIZE + 2*BORDER)*3)
40
#else
41
 #define HEIGHT WIDTH
42
#endif
43

            
44
#define TEXT1   "cairo user-font."
45
#define TEXT2   " zg"
46

            
47
#define END_GLYPH 0
48
#define STROKE 126
49
#define CLOSE 127
50

            
51
/* Simple glyph definition: 1 - 15 means lineto (or moveto for first
52
 * point) for one of the points on this grid:
53
 *
54
 *      1  2  3
55
 *      4  5  6
56
 *      7  8  9
57
 * ----10 11 12----(baseline)
58
 *     13 14 15
59
 */
60
typedef struct {
61
    unsigned long ucs4;
62
    int width;
63
    char data[16];
64
} test_scaled_font_glyph_t;
65

            
66
static cairo_user_data_key_t test_font_face_glyphs_key;
67

            
68
static cairo_status_t
69
6
test_scaled_font_init (cairo_scaled_font_t  *scaled_font,
70
		       cairo_t              *cr,
71
		       cairo_font_extents_t *metrics)
72
{
73
6
  metrics->ascent  = .75;
74
6
  metrics->descent = .25;
75
6
  return CAIRO_STATUS_SUCCESS;
76
}
77

            
78
static cairo_status_t
79
246
test_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font,
80
				   unsigned long        unicode,
81
				   unsigned long       *glyph)
82
{
83
246
    test_scaled_font_glyph_t *glyphs = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
84
								      &test_font_face_glyphs_key);
85
    int i;
86

            
87
2538
    for (i = 0; glyphs[i].ucs4 != (unsigned long) -1; i++)
88
2538
	if (glyphs[i].ucs4 == unicode) {
89
246
	    *glyph = i;
90
246
	    return CAIRO_STATUS_SUCCESS;
91
	}
92

            
93
    /* Not found.  Default to glyph 0 */
94
    return CAIRO_STATUS_SUCCESS;
95
}
96

            
97
static cairo_status_t
98
96
test_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
99
			       unsigned long         glyph,
100
			       cairo_t              *cr,
101
			       cairo_text_extents_t *metrics)
102
{
103
96
    test_scaled_font_glyph_t *glyphs = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
104
								      &test_font_face_glyphs_key);
105
    int i;
106
    const char *data;
107
    div_t d;
108
    double x, y;
109

            
110
    /* FIXME: We simply crash on out-of-bound glyph indices */
111

            
112
96
    metrics->x_advance = glyphs[glyph].width / 4.0;
113

            
114
96
    cairo_set_line_width (cr, 0.1);
115
96
    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
116
96
    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
117

            
118
96
    data = glyphs[glyph].data;
119
630
    for (i = 0; data[i] != END_GLYPH; i++) {
120
534
	switch (data[i]) {
121
114
	case STROKE:
122
114
	    cairo_new_sub_path (cr);
123
114
	    break;
124

            
125
6
	case CLOSE:
126
6
	    cairo_close_path (cr);
127
6
	    break;
128

            
129
414
	default:
130
414
	    d = div (data[i] - 1, 3);
131
414
	    x = d.rem / 4.0 + 0.125;
132
414
	    y = d.quot / 5.0 + 0.4 - 1.0;
133
414
	    cairo_line_to (cr, x, y);
134
	}
135
    }
136
96
    cairo_stroke (cr);
137

            
138
96
    return CAIRO_STATUS_SUCCESS;
139
}
140

            
141

            
142
/* If color_render is TRUE, use the render_color_glyph callback
143
 * instead of the render_glyph callbac. The output should be identical
144
 * in this test since the render function does not alter the cairo_t
145
 * source.
146
 */
147
static cairo_status_t
148
6
_user_font_face_create (cairo_font_face_t **out, cairo_bool_t color_render)
149
{
150
    /* Simple glyph definition: 1 - 15 means lineto (or moveto for first
151
     * point) for one of the points on this grid:
152
     *
153
     *      1  2  3
154
     *      4  5  6
155
     *      7  8  9
156
     * ----10 11 12----(baseline)
157
     *     13 14 15
158
     */
159
    static const test_scaled_font_glyph_t glyphs [] = {
160
	{ 'a',  3, { 4, 6, 12, 10, 7, 9, STROKE, END_GLYPH } },
161
	{ 'c',  3, { 6, 4, 10, 12, STROKE, END_GLYPH } },
162
	{ 'e',  3, { 12, 10, 4, 6, 9, 7, STROKE, END_GLYPH } },
163
	{ 'f',  3, { 3, 2, 11, STROKE, 4, 6, STROKE, END_GLYPH } },
164
	{ 'g',  3, { 12, 10, 4, 6, 15, 13, STROKE, END_GLYPH } },
165
	{ 'h',  3, { 1, 10, STROKE, 7, 5, 6, 12, STROKE, END_GLYPH } },
166
	{ 'i',  1, { 1, 1, STROKE, 4, 10, STROKE, END_GLYPH } },
167
	{ 'l',  1, { 1, 10, STROKE, END_GLYPH } },
168
	{ 'n',  3, { 10, 4, STROKE, 7, 5, 6, 12, STROKE, END_GLYPH } },
169
	{ 'o',  3, { 4, 10, 12, 6, CLOSE, END_GLYPH } },
170
	{ 'r',  3, { 4, 10, STROKE, 7, 5, 6, STROKE, END_GLYPH } },
171
	{ 's',  3, { 6, 4, 7, 9, 12, 10, STROKE, END_GLYPH } },
172
	{ 't',  3, { 2, 11, 12, STROKE, 4, 6, STROKE, END_GLYPH } },
173
	{ 'u',  3, { 4, 10, 12, 6, STROKE, END_GLYPH } },
174
	{ 'z',  3, { 4, 6, 10, 12, STROKE, END_GLYPH } },
175
	{ ' ',  1, { END_GLYPH } },
176
	{ '-',  2, { 7, 8, STROKE, END_GLYPH } },
177
	{ '.',  1, { 10, 10, STROKE, END_GLYPH } },
178
	{  -1,  0, { END_GLYPH } },
179
    };
180

            
181
    cairo_font_face_t *user_font_face;
182
    cairo_status_t status;
183

            
184
6
    user_font_face = cairo_user_font_face_create ();
185
6
    cairo_user_font_face_set_init_func (user_font_face, test_scaled_font_init);
186
6
    if (color_render)
187
3
        cairo_user_font_face_set_render_color_glyph_func (user_font_face, test_scaled_font_render_glyph);
188
    else
189
3
        cairo_user_font_face_set_render_glyph_func (user_font_face, test_scaled_font_render_glyph);
190

            
191
6
    cairo_user_font_face_set_unicode_to_glyph_func (user_font_face, test_scaled_font_unicode_to_glyph);
192

            
193
6
    status = cairo_font_face_set_user_data (user_font_face,
194
					    &test_font_face_glyphs_key,
195
					    (void*) glyphs, NULL);
196
6
    if (status) {
197
	cairo_font_face_destroy (user_font_face);
198
	return status;
199
    }
200

            
201
6
    *out = user_font_face;
202
6
    return CAIRO_STATUS_SUCCESS;
203
}
204

            
205
static void
206
6
draw_line (cairo_t *cr)
207
{
208
    /* TEXT1 in black */
209
6
    cairo_set_source_rgb (cr, 0, 0, 0);
210
6
    cairo_show_text (cr, TEXT1);
211

            
212
    /* Draw TEXT2 three times with three different foreground colors.
213
     * This checks that cairo uses the foreground color and does not cache
214
     * glyph images when the foreground color changes.
215
     */
216
6
    cairo_show_text (cr, TEXT2);
217
6
    cairo_set_source_rgb (cr, 0, 0.5, 0);
218
6
    cairo_show_text (cr, TEXT2);
219
6
    cairo_set_source_rgb (cr, 0.2, 0.5, 0.5);
220
6
    cairo_show_text (cr, TEXT2);
221
6
}
222

            
223
static cairo_test_status_t
224
3
draw (cairo_t *cr, int width, int height)
225
{
226
    cairo_font_face_t *font_face;
227
    char full_text[100];
228
    cairo_font_extents_t font_extents;
229
    cairo_text_extents_t extents;
230
    cairo_status_t status;
231

            
232
3
    strcpy(full_text, TEXT1);
233
3
    strcat(full_text, TEXT2);
234
3
    strcat(full_text, TEXT2);
235
3
    strcat(full_text, TEXT2);
236

            
237
3
    cairo_set_source_rgb (cr, 1, 1, 1);
238
3
    cairo_paint (cr);
239

            
240
#ifdef ROTATED
241
    cairo_translate (cr, TEXT_SIZE, 0);
242
    cairo_rotate (cr, .6);
243
#endif
244

            
245
3
    status = _user_font_face_create (&font_face, FALSE);
246
3
    if (status) {
247
	return cairo_test_status_from_status (cairo_test_get_context (cr),
248
					      status);
249
    }
250

            
251
3
    cairo_set_font_face (cr, font_face);
252
3
    cairo_font_face_destroy (font_face);
253

            
254
3
    cairo_set_font_size (cr, TEXT_SIZE);
255

            
256
3
    cairo_font_extents (cr, &font_extents);
257
3
    cairo_text_extents (cr, full_text, &extents);
258

            
259
    /* logical boundaries in red */
260
3
    cairo_move_to (cr, 0, BORDER);
261
3
    cairo_rel_line_to (cr, WIDTH, 0);
262
3
    cairo_move_to (cr, 0, BORDER + font_extents.ascent);
263
3
    cairo_rel_line_to (cr, WIDTH, 0);
264
3
    cairo_move_to (cr, 0, BORDER + font_extents.ascent + font_extents.descent);
265
3
    cairo_rel_line_to (cr, WIDTH, 0);
266
3
    cairo_move_to (cr, BORDER, 0);
267
3
    cairo_rel_line_to (cr, 0, 2*BORDER + TEXT_SIZE);
268
3
    cairo_move_to (cr, BORDER + extents.x_advance, 0);
269
3
    cairo_rel_line_to (cr, 0, 2*BORDER + TEXT_SIZE);
270
3
    cairo_set_source_rgb (cr, 1, 0, 0);
271
3
    cairo_set_line_width (cr, 2);
272
3
    cairo_stroke (cr);
273

            
274
    /* ink boundaries in green */
275
3
    cairo_rectangle (cr,
276
3
		     BORDER + extents.x_bearing, BORDER + font_extents.ascent + extents.y_bearing,
277
		     extents.width, extents.height);
278
3
    cairo_set_source_rgb (cr, 0, 1, 0);
279
3
    cairo_set_line_width (cr, 2);
280
3
    cairo_stroke (cr);
281

            
282
    /* First line. TEXT1 in black. TEXT2 in different colors. */
283
3
    cairo_move_to (cr, BORDER, BORDER + font_extents.ascent);
284
3
    draw_line (cr);
285

            
286
    /* Now draw the second line using the render_color_glyph
287
     * callback. The text should be all black because the default
288
     * color of render function is used instead of the foreground
289
     * color.
290
     */
291
3
    status = _user_font_face_create (&font_face, TRUE);
292
3
    if (status) {
293
	return cairo_test_status_from_status (cairo_test_get_context (cr),
294
					      status);
295
    }
296

            
297
3
    cairo_set_font_face (cr, font_face);
298
3
    cairo_font_face_destroy (font_face);
299

            
300
3
    cairo_set_font_size (cr, TEXT_SIZE);
301

            
302
3
    cairo_move_to (cr, BORDER, BORDER + font_extents.height + 2*BORDER + font_extents.ascent);
303
3
    draw_line (cr);
304

            
305
    /* Third line. Filled version of text in blue */
306
3
    cairo_set_source_rgb (cr, 0, 0, 1);
307
3
    cairo_move_to (cr, BORDER, BORDER + font_extents.height + 4*BORDER + 2*font_extents.ascent);
308
3
    cairo_text_path (cr, full_text);
309
3
    cairo_fill (cr);
310

            
311
3
    return CAIRO_TEST_SUCCESS;
312
}
313

            
314
1
CAIRO_TEST (user_font,
315
	    "Tests user font feature",
316
	    "font, user-font", /* keywords */
317
	    "cairo >= 1.7.4", /* requirements */
318
	    WIDTH, HEIGHT,
319
	    NULL, draw)