1/*
2 * Copyright (C) 2006 Apple Inc.
3 * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
4 * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com>
5 * Copyright (C) 2007 Holger Hans Peter Freyther
6 * Copyright (C) 2009, 2010 Igalia S.L.
7 * All rights reserved.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB.  If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25#include "config.h"
26#include "FontPlatformData.h"
27
28#include "FontDescription.h"
29#include <cairo-ft.h>
30#include <cairo.h>
31#include <fontconfig/fcfreetype.h>
32#include <ft2build.h>
33#include FT_TRUETYPE_TABLES_H
34#include <wtf/text/WTFString.h>
35
36#if !PLATFORM(EFL)
37#include <gdk/gdk.h>
38#endif
39
40namespace WebCore {
41
42cairo_subpixel_order_t convertFontConfigSubpixelOrder(int fontConfigOrder)
43{
44    switch (fontConfigOrder) {
45    case FC_RGBA_RGB:
46        return CAIRO_SUBPIXEL_ORDER_RGB;
47    case FC_RGBA_BGR:
48        return CAIRO_SUBPIXEL_ORDER_BGR;
49    case FC_RGBA_VRGB:
50        return CAIRO_SUBPIXEL_ORDER_VRGB;
51    case FC_RGBA_VBGR:
52        return CAIRO_SUBPIXEL_ORDER_VBGR;
53    case FC_RGBA_NONE:
54    case FC_RGBA_UNKNOWN:
55        return CAIRO_SUBPIXEL_ORDER_DEFAULT;
56    }
57    return CAIRO_SUBPIXEL_ORDER_DEFAULT;
58}
59
60cairo_hint_style_t convertFontConfigHintStyle(int fontConfigStyle)
61{
62    switch (fontConfigStyle) {
63    case FC_HINT_NONE:
64        return CAIRO_HINT_STYLE_NONE;
65    case FC_HINT_SLIGHT:
66        return CAIRO_HINT_STYLE_SLIGHT;
67    case FC_HINT_MEDIUM:
68        return CAIRO_HINT_STYLE_MEDIUM;
69    case FC_HINT_FULL:
70        return CAIRO_HINT_STYLE_FULL;
71    }
72    return CAIRO_HINT_STYLE_NONE;
73}
74
75void setCairoFontOptionsFromFontConfigPattern(cairo_font_options_t* options, FcPattern* pattern)
76{
77    FcBool booleanResult;
78    int integerResult;
79
80    if (FcPatternGetInteger(pattern, FC_RGBA, 0, &integerResult) == FcResultMatch) {
81        cairo_font_options_set_subpixel_order(options, convertFontConfigSubpixelOrder(integerResult));
82
83        // Based on the logic in cairo-ft-font.c in the cairo source, a font with
84        // a subpixel order implies that is uses subpixel antialiasing.
85        if (integerResult != FC_RGBA_NONE)
86            cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_SUBPIXEL);
87    }
88
89    if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &booleanResult) == FcResultMatch) {
90        // Only override the anti-aliasing setting if was previously turned off. Otherwise
91        // we'll override the preference which decides between gray anti-aliasing and
92        // subpixel anti-aliasing.
93        if (!booleanResult)
94            cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_NONE);
95        else if (cairo_font_options_get_antialias(options) == CAIRO_ANTIALIAS_NONE)
96            cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY);
97    }
98
99    if (FcPatternGetInteger(pattern, FC_HINT_STYLE, 0, &integerResult) == FcResultMatch)
100        cairo_font_options_set_hint_style(options, convertFontConfigHintStyle(integerResult));
101    if (FcPatternGetBool(pattern, FC_HINTING, 0, &booleanResult) == FcResultMatch && !booleanResult)
102        cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);
103}
104
105static cairo_font_options_t* getDefaultFontOptions()
106{
107#if PLATFORM(GTK)
108    if (GdkScreen* screen = gdk_screen_get_default()) {
109        const cairo_font_options_t* screenOptions = gdk_screen_get_font_options(screen);
110        if (screenOptions)
111            return cairo_font_options_copy(screenOptions);
112    }
113#endif
114    return cairo_font_options_create();
115}
116
117static void rotateCairoMatrixForVerticalOrientation(cairo_matrix_t* matrix)
118{
119    // The resulting transformation matrix for vertical glyphs (V) is a
120    // combination of rotation (R) and translation (T) applied on the
121    // horizontal matrix (H). V = H . R . T, where R rotates by -90 degrees
122    // and T translates by font size towards y axis.
123    cairo_matrix_rotate(matrix, -M_PI_2);
124    cairo_matrix_translate(matrix, 0.0, 1.0);
125}
126
127FontPlatformData::FontPlatformData(FcPattern* pattern, const FontDescription& fontDescription)
128    : m_pattern(pattern)
129    , m_fallbacks(0)
130    , m_size(fontDescription.computedPixelSize())
131    , m_syntheticBold(false)
132    , m_syntheticOblique(false)
133    , m_fixedWidth(false)
134    , m_scaledFont(0)
135    , m_orientation(fontDescription.orientation())
136{
137    RefPtr<cairo_font_face_t> fontFace = adoptRef(cairo_ft_font_face_create_for_pattern(m_pattern.get()));
138    initializeWithFontFace(fontFace.get(), fontDescription);
139
140    int spacing;
141    if (FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing) == FcResultMatch && spacing == FC_MONO)
142        m_fixedWidth = true;
143
144    if (fontDescription.weight() >= FontWeightBold) {
145        // The FC_EMBOLDEN property instructs us to fake the boldness of the font.
146        FcBool fontConfigEmbolden = FcFalse;
147        if (FcPatternGetBool(pattern, FC_EMBOLDEN, 0, &fontConfigEmbolden) == FcResultMatch)
148            m_syntheticBold = fontConfigEmbolden;
149
150        // Fallback fonts may not have FC_EMBOLDEN activated even though it's necessary.
151        int weight = 0;
152        if (!m_syntheticBold && FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) == FcResultMatch)
153            m_syntheticBold = m_syntheticBold || weight < FC_WEIGHT_DEMIBOLD;
154    }
155}
156
157FontPlatformData::FontPlatformData(float size, bool bold, bool italic)
158    : m_fallbacks(0)
159    , m_size(size)
160    , m_syntheticBold(bold)
161    , m_syntheticOblique(italic)
162    , m_fixedWidth(false)
163    , m_scaledFont(0)
164    , m_orientation(Horizontal)
165{
166    // We cannot create a scaled font here.
167}
168
169FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, float size, bool bold, bool italic, FontOrientation orientation)
170    : m_fallbacks(0)
171    , m_size(size)
172    , m_syntheticBold(bold)
173    , m_syntheticOblique(italic)
174    , m_fixedWidth(false)
175    , m_scaledFont(0)
176    , m_orientation(orientation)
177{
178    initializeWithFontFace(fontFace);
179
180    FT_Face fontConfigFace = cairo_ft_scaled_font_lock_face(m_scaledFont);
181    if (fontConfigFace) {
182        m_fixedWidth = fontConfigFace->face_flags & FT_FACE_FLAG_FIXED_WIDTH;
183        cairo_ft_scaled_font_unlock_face(m_scaledFont);
184    }
185}
186
187FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other)
188{
189    // Check for self-assignment.
190    if (this == &other)
191        return *this;
192
193    m_size = other.m_size;
194    m_syntheticBold = other.m_syntheticBold;
195    m_syntheticOblique = other.m_syntheticOblique;
196    m_fixedWidth = other.m_fixedWidth;
197    m_pattern = other.m_pattern;
198    m_orientation = other.m_orientation;
199    m_horizontalOrientationMatrix = other.m_horizontalOrientationMatrix;
200
201    if (m_fallbacks) {
202        FcFontSetDestroy(m_fallbacks);
203        // This will be re-created on demand.
204        m_fallbacks = 0;
205    }
206
207    if (m_scaledFont && m_scaledFont != hashTableDeletedFontValue())
208        cairo_scaled_font_destroy(m_scaledFont);
209    m_scaledFont = cairo_scaled_font_reference(other.m_scaledFont);
210
211    m_harfBuzzFace = other.m_harfBuzzFace;
212
213    return *this;
214}
215
216FontPlatformData::FontPlatformData(const FontPlatformData& other)
217    : m_fallbacks(0)
218    , m_scaledFont(0)
219    , m_harfBuzzFace(other.m_harfBuzzFace)
220{
221    *this = other;
222}
223
224FontPlatformData::FontPlatformData(const FontPlatformData& other, float size)
225    : m_harfBuzzFace(other.m_harfBuzzFace)
226{
227    *this = other;
228
229    // We need to reinitialize the instance, because the difference in size
230    // necessitates a new scaled font instance.
231    m_size = size;
232    initializeWithFontFace(cairo_scaled_font_get_font_face(m_scaledFont));
233}
234
235FontPlatformData::~FontPlatformData()
236{
237    if (m_fallbacks) {
238        FcFontSetDestroy(m_fallbacks);
239        m_fallbacks = 0;
240    }
241
242    if (m_scaledFont && m_scaledFont != hashTableDeletedFontValue())
243        cairo_scaled_font_destroy(m_scaledFont);
244}
245
246HarfBuzzFace* FontPlatformData::harfBuzzFace() const
247{
248    if (!m_harfBuzzFace)
249        m_harfBuzzFace = HarfBuzzFace::create(const_cast<FontPlatformData*>(this), hash());
250
251    return m_harfBuzzFace.get();
252}
253
254bool FontPlatformData::isFixedPitch()
255{
256    return m_fixedWidth;
257}
258
259bool FontPlatformData::operator==(const FontPlatformData& other) const
260{
261    // FcPatternEqual does not support null pointers as arguments.
262    if ((m_pattern && !other.m_pattern)
263        || (!m_pattern && other.m_pattern)
264        || (m_pattern != other.m_pattern && !FcPatternEqual(m_pattern.get(), other.m_pattern.get())))
265        return false;
266
267    return m_scaledFont == other.m_scaledFont
268        && m_size == other.m_size
269        && m_syntheticOblique == other.m_syntheticOblique
270        && m_orientation == other.m_orientation
271        && m_syntheticBold == other.m_syntheticBold;
272}
273
274#ifndef NDEBUG
275String FontPlatformData::description() const
276{
277    return String();
278}
279#endif
280
281void FontPlatformData::initializeWithFontFace(cairo_font_face_t* fontFace, const FontDescription& fontDescription)
282{
283    cairo_font_options_t* options = getDefaultFontOptions();
284
285    cairo_matrix_t ctm;
286    cairo_matrix_init_identity(&ctm);
287
288    // Scaling a font with width zero size leads to a failed cairo_scaled_font_t instantiations.
289    // Instead we scale we scale the font to a very tiny size and just abort rendering later on.
290    float realSize = m_size ? m_size : 1;
291
292    cairo_matrix_t fontMatrix;
293    if (!m_pattern)
294        cairo_matrix_init_scale(&fontMatrix, realSize, realSize);
295    else {
296        setCairoFontOptionsFromFontConfigPattern(options, m_pattern.get());
297
298        // FontConfig may return a list of transformation matrices with the pattern, for instance,
299        // for fonts that are oblique. We use that to initialize the cairo font matrix.
300        FcMatrix fontConfigMatrix, *tempFontConfigMatrix;
301        FcMatrixInit(&fontConfigMatrix);
302
303        // These matrices may be stacked in the pattern, so it's our job to get them all and multiply them.
304        for (int i = 0; FcPatternGetMatrix(m_pattern.get(), FC_MATRIX, i, &tempFontConfigMatrix) == FcResultMatch; i++)
305            FcMatrixMultiply(&fontConfigMatrix, &fontConfigMatrix, tempFontConfigMatrix);
306        cairo_matrix_init(&fontMatrix, fontConfigMatrix.xx, -fontConfigMatrix.yx,
307                          -fontConfigMatrix.xy, fontConfigMatrix.yy, 0, 0);
308
309        // We requested an italic font, but Fontconfig gave us one that was neither oblique nor italic.
310        int actualFontSlant;
311        if (fontDescription.italic() && FcPatternGetInteger(m_pattern.get(), FC_SLANT, 0, &actualFontSlant) == FcResultMatch)
312            m_syntheticOblique = actualFontSlant == FC_SLANT_ROMAN;
313
314        // The matrix from FontConfig does not include the scale.
315        cairo_matrix_scale(&fontMatrix, realSize, realSize);
316    }
317
318    if (syntheticOblique()) {
319        static const float syntheticObliqueSkew = -tanf(14 * acosf(0) / 90);
320        cairo_matrix_t skew = {1, 0, syntheticObliqueSkew, 1, 0, 0};
321        cairo_matrix_multiply(&fontMatrix, &skew, &fontMatrix);
322    }
323
324    m_horizontalOrientationMatrix = fontMatrix;
325    if (m_orientation == Vertical)
326        rotateCairoMatrixForVerticalOrientation(&fontMatrix);
327
328    m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options);
329    cairo_font_options_destroy(options);
330}
331
332bool FontPlatformData::hasCompatibleCharmap()
333{
334    ASSERT(m_scaledFont);
335    FT_Face freeTypeFace = cairo_ft_scaled_font_lock_face(m_scaledFont);
336    bool hasCompatibleCharmap = !(FT_Select_Charmap(freeTypeFace, ft_encoding_unicode)
337                                && FT_Select_Charmap(freeTypeFace, ft_encoding_symbol)
338                                && FT_Select_Charmap(freeTypeFace, ft_encoding_apple_roman));
339    cairo_ft_scaled_font_unlock_face(m_scaledFont);
340    return hasCompatibleCharmap;
341}
342
343PassRefPtr<OpenTypeVerticalData> FontPlatformData::verticalData() const
344{
345    ASSERT(hash());
346    return fontCache().getVerticalData(String::number(hash()), *this);
347}
348
349PassRefPtr<SharedBuffer> FontPlatformData::openTypeTable(uint32_t table) const
350{
351    FT_Face freeTypeFace = cairo_ft_scaled_font_lock_face(m_scaledFont);
352    if (!freeTypeFace)
353        return 0;
354
355    FT_ULong tableSize = 0;
356    // Tag bytes need to be reversed because OT_MAKE_TAG uses big-endian order.
357    uint32_t tag = FT_MAKE_TAG((table & 0xff), (table & 0xff00) >> 8, (table & 0xff0000) >> 16, table >> 24);
358    if (FT_Load_Sfnt_Table(freeTypeFace, tag, 0, 0, &tableSize))
359        return 0;
360
361    RefPtr<SharedBuffer> buffer = SharedBuffer::create(tableSize);
362    FT_ULong expectedTableSize = tableSize;
363    if (buffer->size() != tableSize)
364        return 0;
365
366    FT_Error error = FT_Load_Sfnt_Table(freeTypeFace, tag, 0, reinterpret_cast<FT_Byte*>(const_cast<char*>(buffer->data())), &tableSize);
367    if (error || tableSize != expectedTableSize)
368        return 0;
369
370    cairo_ft_scaled_font_unlock_face(m_scaledFont);
371
372    return buffer.release();
373}
374
375void FontPlatformData::setOrientation(FontOrientation orientation)
376{
377    ASSERT(m_scaledFont);
378
379    if (!m_scaledFont || (m_orientation == orientation))
380        return;
381
382    cairo_matrix_t transformationMatrix;
383    cairo_matrix_init_identity(&transformationMatrix);
384
385    cairo_matrix_t fontMatrix;
386    cairo_scaled_font_get_font_matrix(m_scaledFont, &fontMatrix);
387
388    cairo_font_options_t* options = getDefaultFontOptions();
389
390    // In case of vertical orientation, rotate the transformation matrix.
391    // Otherwise restore the horizontal orientation matrix.
392    if (orientation == Vertical)
393        rotateCairoMatrixForVerticalOrientation(&fontMatrix);
394    else
395        fontMatrix = m_horizontalOrientationMatrix;
396
397    cairo_font_face_t* fontFace = cairo_scaled_font_get_font_face(m_scaledFont);
398    cairo_scaled_font_destroy(m_scaledFont);
399    m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &transformationMatrix, options);
400    cairo_font_options_destroy(options);
401    m_orientation = orientation;
402}
403
404}
405