1
/*
2
 * Copyright © 2008 Jeff Muizelaar
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
 * Jeff Muizelaar not be used in advertising or publicity pertaining to
10
 * distribution of the software without specific, written prior
11
 * permission. Jeff Muizelaar 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
 * JEFF MUIZELAAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17
 * FITNESS, IN NO EVENT SHALL JEFF MUIZELAAR 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
 *	Jeff Muizelaar <jeff@infidigm.net>
25
 *	Kristian Høgsberg <krh@redhat.com>
26
 *	Behdad Esfahbod <behdad@behdad.org>
27
 */
28

            
29
#include "cairo-test.h"
30

            
31
#include <math.h>
32

            
33
#define BORDER 10
34
#define TEXT_SIZE 32
35
#define WIDTH  (TEXT_SIZE * 13.75 + 2*BORDER)
36
#define HEIGHT ((TEXT_SIZE + 2*BORDER)*3 + BORDER)
37
#define TEXT   "test of rescaled glyphs"
38

            
39
static const cairo_user_data_key_t rescale_font_closure_key;
40

            
41
struct rescaled_font {
42
    cairo_font_face_t *substitute_font;
43
    cairo_scaled_font_t *measuring_font;
44
    unsigned long glyph_count;
45
    unsigned long start;
46
    double *desired_width;
47
    double *rescale_factor;
48
};
49

            
50
static cairo_status_t
51
45
test_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
52
			       unsigned long         glyph,
53
			       cairo_t              *cr,
54
			       cairo_text_extents_t *metrics)
55
{
56
    cairo_font_face_t *user_font;
57
    struct rescaled_font *r;
58
    cairo_glyph_t cairo_glyph;
59

            
60
45
    cairo_glyph.index = glyph;
61
45
    cairo_glyph.x = 0;
62
45
    cairo_glyph.y = 0;
63

            
64
45
    user_font = cairo_scaled_font_get_font_face (scaled_font);
65
45
    r = cairo_font_face_get_user_data (user_font, &rescale_font_closure_key);
66
45
    cairo_set_font_face (cr, r->substitute_font);
67

            
68
45
    if (glyph - r->start < r->glyph_count) {
69
	cairo_matrix_t matrix;
70

            
71
45
	if (isnan (r->rescale_factor[glyph - r->start])) {
72
	    double desired_width;
73
	    double actual_width;
74
	    cairo_text_extents_t extents;
75

            
76
	    /* measure the glyph and compute the necessary rescaling factor */
77
45
	    cairo_scaled_font_glyph_extents (r->measuring_font,
78
					     &cairo_glyph, 1,
79
					     &extents);
80

            
81
45
	    desired_width = r->desired_width[glyph - r->start];
82
45
	    actual_width = extents.x_advance;
83

            
84
45
	    r->rescale_factor[glyph - r->start] = desired_width / actual_width;
85
	}
86

            
87
	/* scale the font so that the glyph width matches the desired width */
88
45
	cairo_get_font_matrix (cr, &matrix);
89
45
	cairo_matrix_scale (&matrix, r->rescale_factor[glyph - r->start], 1.);
90
45
	cairo_set_font_matrix (cr, &matrix);
91
    }
92

            
93
45
    cairo_show_glyphs (cr, &cairo_glyph, 1);
94
45
    cairo_glyph_extents (cr, &cairo_glyph, 1, metrics);
95

            
96
45
    return CAIRO_STATUS_SUCCESS;
97
}
98

            
99
static void
100
45
unichar_to_utf8 (uint32_t ucs4, char utf8[7])
101
{
102
    int i, charlen, first;
103

            
104
45
    if (ucs4 < 0x80) {
105
45
	first = 0;
106
45
	charlen = 1;
107
    } else if (ucs4 < 0x800) {
108
	first = 0xc0;
109
	charlen = 2;
110
    } else if (ucs4 < 0x10000) {
111
	first = 0xe0;
112
	charlen = 3;
113
    } else if (ucs4 < 0x200000) {
114
	first = 0xf0;
115
	charlen = 4;
116
    } else if (ucs4 < 0x4000000) {
117
	first = 0xf8;
118
	charlen = 5;
119
    } else {
120
	first = 0xfc;
121
	charlen = 6;
122
    }
123

            
124
45
    for (i = charlen - 1; i > 0; --i) {
125
	utf8[i] = (ucs4 & 0x3f) | 0x80;
126
	ucs4 >>= 6;
127
    }
128
45
    utf8[0] = ucs4 | first;
129
45
    utf8[charlen] = '\0';
130
45
}
131

            
132
static cairo_status_t
133
45
test_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font,
134
				   unsigned long        unicode,
135
				   unsigned long       *glyph_index)
136
{
137
    cairo_font_face_t *user_font;
138
    struct rescaled_font *r;
139
    int num_glyphs;
140
45
    cairo_glyph_t *glyphs = NULL;
141
    cairo_status_t status;
142
    char utf8[7];
143

            
144
45
    user_font = cairo_scaled_font_get_font_face (scaled_font);
145

            
146
45
    unichar_to_utf8 (unicode, utf8);
147
45
    r = cairo_font_face_get_user_data (user_font, &rescale_font_closure_key);
148
45
    status  = cairo_scaled_font_text_to_glyphs (r->measuring_font, 0, 0,
149
						utf8, -1,
150
						&glyphs, &num_glyphs,
151
						NULL, NULL, NULL);
152
45
    if (status)
153
	return status;
154

            
155
45
    *glyph_index = glyphs[0].index;
156

            
157
45
    cairo_glyph_free (glyphs);
158
45
    return CAIRO_STATUS_SUCCESS;
159
}
160

            
161
static void rescale_font_closure_destroy (void *data)
162
{
163
    struct rescaled_font *r = data;
164

            
165
    cairo_font_face_destroy (r->substitute_font);
166
    cairo_scaled_font_destroy (r->measuring_font);
167
    free (r->desired_width);
168
    free (r->rescale_factor);
169
    free (r);
170
}
171

            
172
static cairo_status_t
173
3
create_rescaled_font (cairo_font_face_t *substitute_font,
174
		      int glyph_start,
175
		      int glyph_count,
176
		      double *desired_width,
177
		      cairo_font_face_t **out)
178
{
179
    cairo_font_face_t *user_font_face;
180
    struct rescaled_font *r;
181
    cairo_font_options_t *options;
182
    cairo_status_t status;
183
    cairo_matrix_t m;
184
    unsigned long i;
185

            
186
3
    user_font_face = cairo_user_font_face_create ();
187
3
    cairo_user_font_face_set_render_glyph_func (user_font_face, test_scaled_font_render_glyph);
188
3
    cairo_user_font_face_set_unicode_to_glyph_func (user_font_face, test_scaled_font_unicode_to_glyph);
189

            
190
3
    r = xmalloc (sizeof (struct rescaled_font));
191
3
    r->substitute_font = cairo_font_face_reference (substitute_font);
192

            
193
    /* we don't want any hinting when doing the measuring */
194
3
    options = cairo_font_options_create ();
195
3
    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
196
3
    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
197

            
198
3
    cairo_matrix_init_identity (&m);
199

            
200
3
    r->measuring_font = cairo_scaled_font_create (r->substitute_font,
201
						  &m, &m,
202
						  options);
203
3
    cairo_font_options_destroy (options);
204

            
205

            
206
3
    r->start = glyph_start;
207
3
    r->glyph_count = glyph_count;
208
3
    r->desired_width = xcalloc (sizeof (double), r->glyph_count);
209
3
    r->rescale_factor = xcalloc (sizeof (double), r->glyph_count);
210

            
211
273
    for (i = 0; i < r->glyph_count; i++) {
212
270
	r->desired_width[i] = desired_width[i];
213
	/* use NaN to specify unset */
214
270
	r->rescale_factor[i] = cairo_test_NaN ();
215
    }
216

            
217
3
    status = cairo_font_face_set_user_data (user_font_face,
218
					    &rescale_font_closure_key,
219
					    r, rescale_font_closure_destroy);
220
3
    if (status) {
221
	rescale_font_closure_destroy (r);
222
	cairo_font_face_destroy (user_font_face);
223
	return status;
224
    }
225

            
226
3
    *out = user_font_face;
227
3
    return CAIRO_STATUS_SUCCESS;
228
}
229

            
230
static cairo_status_t
231
3
get_user_font_face (cairo_font_face_t *substitute_font,
232
		    const char *text,
233
		    cairo_font_face_t *old,
234
		    cairo_font_face_t **out)
235
{
236
    cairo_font_options_t *options;
237
    cairo_matrix_t m;
238
    cairo_scaled_font_t *measure;
239
    int i;
240
    double *widths;
241
    int count;
242
    int num_glyphs;
243
    unsigned long min_index, max_index;
244
    cairo_status_t status;
245

            
246
3
    cairo_glyph_t *glyphs = NULL;
247

            
248
    /* we don't want any hinting when doing the measuring */
249
3
    options = cairo_font_options_create ();
250
3
    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
251
3
    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
252

            
253
3
    cairo_matrix_init_identity (&m);
254
3
    measure = cairo_scaled_font_create (old, &m, &m, options);
255

            
256
3
    status = cairo_scaled_font_text_to_glyphs (measure, 0, 0,
257
					       text, -1,
258
					       &glyphs, &num_glyphs,
259
					       NULL, NULL, NULL);
260
3
    cairo_font_options_destroy (options);
261

            
262
3
    if (status) {
263
	cairo_scaled_font_destroy (measure);
264
	return status;
265
    }
266

            
267
    /* find the glyph range the text covers */
268
3
    max_index = glyphs[0].index;
269
3
    min_index = glyphs[0].index;
270
72
    for (i=0; i<num_glyphs; i++) {
271
69
	if (glyphs[i].index < min_index)
272
6
	    min_index = glyphs[i].index;
273
69
	if (glyphs[i].index > max_index)
274
3
	    max_index = glyphs[i].index;
275
    }
276

            
277
3
    count = max_index - min_index + 1;
278
3
    widths = xcalloc (sizeof (double), count);
279
    /* measure all of the necessary glyphs individually */
280
72
    for (i=0; i<num_glyphs; i++) {
281
	cairo_text_extents_t extents;
282
69
	cairo_scaled_font_glyph_extents (measure, &glyphs[i], 1, &extents);
283
69
	widths[glyphs[i].index - min_index] = extents.x_advance;
284
    }
285

            
286
3
    status = cairo_scaled_font_status (measure);
287
3
    cairo_scaled_font_destroy (measure);
288
3
    cairo_glyph_free (glyphs);
289

            
290
3
    if (status == CAIRO_STATUS_SUCCESS) {
291
3
	status = create_rescaled_font (substitute_font,
292
				       min_index, count, widths,
293
				       out);
294
    }
295

            
296
3
    free (widths);
297
3
    return status;
298
}
299

            
300
static cairo_test_status_t
301
3
draw (cairo_t *cr, int width, int height)
302
{
303
    cairo_font_extents_t font_extents;
304
    cairo_text_extents_t extents;
305
    cairo_font_face_t *rescaled;
306
    cairo_font_face_t *old;
307
    cairo_font_face_t *substitute;
308
3
    const char text[] = TEXT;
309
    cairo_status_t status;
310

            
311
3
    cairo_set_source_rgb (cr, 1, 1, 1);
312
3
    cairo_paint (cr);
313

            
314
3
    cairo_select_font_face (cr,
315
			    CAIRO_TEST_FONT_FAMILY " Sans",
316
			    CAIRO_FONT_SLANT_NORMAL,
317
			    CAIRO_FONT_WEIGHT_NORMAL);
318

            
319
3
    cairo_set_font_size (cr, TEXT_SIZE);
320

            
321
3
    cairo_font_extents (cr, &font_extents);
322
3
    cairo_text_extents (cr, text, &extents);
323

            
324
3
    cairo_set_source_rgb (cr, 0, 0, 0);
325
3
    cairo_move_to (cr, BORDER, BORDER + font_extents.ascent);
326
3
    cairo_show_text (cr, text);
327

            
328
    /* same text in 'mono' with widths that match the 'sans' version */
329
3
    old = cairo_font_face_reference (cairo_get_font_face (cr));
330
3
    cairo_select_font_face (cr,
331
			    CAIRO_TEST_FONT_FAMILY " Sans Mono",
332
			    CAIRO_FONT_SLANT_NORMAL,
333
			    CAIRO_FONT_WEIGHT_NORMAL);
334
3
    substitute = cairo_get_font_face (cr);
335

            
336
3
    status = get_user_font_face (substitute, text, old, &rescaled);
337
3
    cairo_font_face_destroy (old);
338
3
    if (status) {
339
	return cairo_test_status_from_status (cairo_test_get_context (cr),
340
					      status);
341
    }
342

            
343
3
    cairo_set_font_face (cr, rescaled);
344
3
    cairo_font_face_destroy (rescaled);
345

            
346
3
    cairo_set_source_rgb (cr, 0, 0, 1);
347
3
    cairo_move_to (cr, BORDER, BORDER + font_extents.height + 2*BORDER + font_extents.ascent);
348
3
    cairo_show_text (cr, text);
349

            
350
    /* mono text */
351
3
    cairo_select_font_face (cr,
352
			    CAIRO_TEST_FONT_FAMILY " Sans Mono",
353
			    CAIRO_FONT_SLANT_NORMAL,
354
			    CAIRO_FONT_WEIGHT_NORMAL);
355

            
356
3
    cairo_set_source_rgb (cr, 0, 0, 1);
357
3
    cairo_move_to (cr, BORDER, BORDER + 2*font_extents.height + 4*BORDER + font_extents.ascent);
358
3
    cairo_show_text (cr, text);
359

            
360
3
    return CAIRO_TEST_SUCCESS;
361
}
362

            
363
1
CAIRO_TEST (user_font_rescale,
364
	    "Tests drawing text with user defined widths",
365
	    "user-font, font", /* keywords */
366
	    NULL, /* requirements */
367
	    WIDTH, HEIGHT,
368
	    NULL, draw)