1
/*
2
 * Copyright © 2017 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
 * Author: MAtthias Clasen <mclasen@redhat.com>
24
 */
25

            
26
#include "cairo-test.h"
27

            
28
#include <assert.h>
29

            
30
/* This test requires freetype2 */
31
#include <ft2build.h>
32
#include FT_FREETYPE_H
33
#include FT_MULTIPLE_MASTERS_H
34

            
35
#if CAIRO_HAS_FC_FONT
36
#include <fontconfig/fontconfig.h>
37
#endif
38

            
39
#include "cairo-ft.h"
40

            
41
#define FloatToFixed(f) ((FT_Fixed)((f)*65536))
42

            
43
static cairo_test_status_t
44
1
test_variation (cairo_test_context_t *ctx,
45
                const char           *input,
46
                const char           *tag,
47
                int                   def,
48
                float                 expected_value)
49
{
50
#if CAIRO_HAS_FC_FONT
51
1
    cairo_test_status_t result = CAIRO_TEST_FAILURE;
52

            
53
1
    cairo_font_face_t *font_face = NULL;
54
1
    cairo_scaled_font_t *scaled_font = NULL;
55
    cairo_matrix_t matrix;
56
1
    cairo_font_options_t *options = NULL;
57
    cairo_status_t status;
58

            
59
1
    FT_Face ft_face = NULL;
60
    FT_MM_Var *ft_mm_var;
61
    FT_Error ret;
62
    FT_Fixed coords[20];
63
    unsigned int i;
64

            
65
    FcPattern *pattern;
66

            
67
    /* we need a font that has variations */
68
1
    pattern = FcPatternBuild (NULL,
69
                              FC_FAMILY, FcTypeString, (FcChar8*)"Adobe Variable Font Prototype",
70
                              NULL);
71
1
    font_face = cairo_ft_font_face_create_for_pattern (pattern);
72
1
    status = cairo_font_face_status (font_face);
73
1
    FcPatternDestroy (pattern);
74

            
75
1
    if (status != CAIRO_STATUS_SUCCESS) {
76
        cairo_test_log (ctx, "Failed to create font face");
77
        goto cleanup;
78
    }
79

            
80
1
    cairo_matrix_init_identity (&matrix);
81
1
    options = cairo_font_options_create ();
82
1
    if (cairo_font_options_status (options) != CAIRO_STATUS_SUCCESS) {
83
        cairo_test_log (ctx, "Failed to create font options");
84
        goto cleanup;
85
    }
86

            
87
1
    cairo_font_options_set_variations (options, input);
88
1
    if (cairo_font_options_status (options) != CAIRO_STATUS_SUCCESS) {
89
        cairo_test_log (ctx, "Failed to set variations");
90
        goto cleanup;
91
    }
92

            
93
1
    if (strcmp (cairo_font_options_get_variations (options), input) != 0) {
94
        cairo_test_log (ctx, "Failed to verify variations");
95
        goto cleanup;
96
    }
97

            
98
1
    scaled_font = cairo_scaled_font_create (font_face, &matrix, &matrix, options);
99
1
    status = cairo_scaled_font_status (scaled_font);
100

            
101
1
    if (status != CAIRO_STATUS_SUCCESS) {
102
        cairo_test_log (ctx, "Failed to create scaled font");
103
        goto cleanup;
104
    }
105

            
106
1
    ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
107
1
    if (cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS) {
108
        cairo_test_log (ctx, "Failed to get FT_Face");
109
        goto cleanup;
110
    }
111
1
    if (strcmp (ft_face->family_name, "Adobe Variable Font Prototype") != 0) {
112
1
        cairo_test_log (ctx, "This test requires the font \"Adobe Variable Font Prototype\" (https://github.com/adobe-fonts/adobe-variable-font-prototype/releases)");
113
1
        result = CAIRO_TEST_UNTESTED;
114
1
        goto cleanup;
115
    }
116

            
117
    ret = FT_Get_MM_Var (ft_face, &ft_mm_var);
118
    if (ret != 0) {
119
        cairo_test_log (ctx, "Failed to get MM");
120
        goto cleanup;
121
    }
122

            
123
    ret = FT_Get_Var_Design_Coordinates (ft_face, 20, coords);
124
    if (ret != 0) {
125
        cairo_test_log (ctx, "Failed to get coords");
126
        goto cleanup;
127
    }
128

            
129
    for (i = 0; i < ft_mm_var->num_axis; i++) {
130
        FT_Var_Axis *axis = &ft_mm_var->axis[i];
131
        cairo_test_log (ctx, "axis %s, value %g\n", axis->name, coords[i] / 65536.);
132
    }
133
    for (i = 0; i < ft_mm_var->num_axis; i++) {
134
        FT_Var_Axis *axis = &ft_mm_var->axis[i];
135
        if (axis->tag == FT_MAKE_TAG(tag[0], tag[1], tag[2], tag[3])) {
136
            if (def) {
137
                if (coords[i] != axis->def) {
138
                    cairo_test_log (ctx, "Axis %s: not default value (%g != %g)",
139
                                    axis->name, coords[i] / 65536., axis->def / 65536.);
140
                    goto cleanup;
141
                }
142
            }
143
            else {
144
                if (coords[i] != FloatToFixed(expected_value)) {
145
                    cairo_test_log (ctx, "Axis %s: not expected value (%g != %g)",
146
                                    axis->name, coords[i] / 65536., expected_value);
147
                    goto cleanup;
148
                }
149
            }
150
        }
151
        else {
152
        }
153
    }
154

            
155
    result = CAIRO_TEST_SUCCESS;
156

            
157
1
cleanup:
158
1
    if (ft_face)
159
1
        cairo_ft_scaled_font_unlock_face (scaled_font);
160
1
    if (scaled_font)
161
1
        cairo_scaled_font_destroy (scaled_font);
162
1
    if (options)
163
1
        cairo_font_options_destroy (options);
164
1
    if (font_face)
165
1
        cairo_font_face_destroy (font_face);
166

            
167
1
    return result;
168
#else
169
    return CAIRO_TEST_UNTESTED;
170
#endif
171
}
172

            
173
static cairo_test_status_t
174
1
preamble (cairo_test_context_t *ctx)
175
{
176
1
    cairo_test_status_t status = CAIRO_TEST_SUCCESS;
177
    struct { const char *input;
178
             const char *tag;
179
             int expected_default;
180
             float expected_value;
181
1
    } tests[] = {
182
      { "wdth=200,wght=300", "wght", 0, 300.0 }, // valid
183
      { "wdth=200.5,wght=300.5", "wght", 0, 300.5 }, // valid, using decimal dot
184
      { "wdth 200 , wght 300", "wght", 0, 300.0 }, // valid, without =
185
      { "wght = 200", "wght", 0, 200.0 }, // valid, whitespace and =
186
      { "CNTR=20", "wght", 1, 0.0 }, // valid, not setting wght
187
      { "weight=100", "wght", 1, 0.0 }, // not a valid tag
188
      { NULL, 0 }
189
    };
190
    int i;
191

            
192
1
    for (i = 0; tests[i].input; i++) {
193
1
       status = test_variation (ctx,
194
                                tests[i].input,
195
                                tests[i].tag,
196
                                tests[i].expected_default,
197
                                tests[i].expected_value);
198
1
       if (status != CAIRO_TEST_SUCCESS)
199
1
           return status;
200
    }
201

            
202
    return CAIRO_TEST_SUCCESS;
203
}
204

            
205
1
CAIRO_TEST (font_variations,
206
	    "Test font variations",
207
	    "fonts", /* keywords */
208
	    NULL, /* requirements */
209
	    9, 11,
210
	    preamble, NULL)