1 /*
2  * Copyright (C) 2006 Apple Computer, 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 "PlatformString.h"
29 #include "FontDescription.h"
30 #include <cairo-ft.h>
31 #include <cairo.h>
32 #include <fontconfig/fcfreetype.h>
33 
34 #if !PLATFORM(EFL)
35 #include <gdk/gdk.h>
36 #endif
37 
38 namespace WebCore {
39 
convertFontConfigSubpixelOrder(int fontConfigOrder)40 cairo_subpixel_order_t convertFontConfigSubpixelOrder(int fontConfigOrder)
41 {
42     switch (fontConfigOrder) {
43     case FC_RGBA_RGB:
44         return CAIRO_SUBPIXEL_ORDER_RGB;
45     case FC_RGBA_BGR:
46         return CAIRO_SUBPIXEL_ORDER_BGR;
47     case FC_RGBA_VRGB:
48         return CAIRO_SUBPIXEL_ORDER_VRGB;
49     case FC_RGBA_VBGR:
50         return CAIRO_SUBPIXEL_ORDER_VBGR;
51     case FC_RGBA_NONE:
52     case FC_RGBA_UNKNOWN:
53         return CAIRO_SUBPIXEL_ORDER_DEFAULT;
54     }
55     return CAIRO_SUBPIXEL_ORDER_DEFAULT;
56 }
57 
convertFontConfigHintStyle(int fontConfigStyle)58 cairo_hint_style_t convertFontConfigHintStyle(int fontConfigStyle)
59 {
60     switch (fontConfigStyle) {
61     case FC_HINT_NONE:
62         return CAIRO_HINT_STYLE_NONE;
63     case FC_HINT_SLIGHT:
64         return CAIRO_HINT_STYLE_SLIGHT;
65     case FC_HINT_MEDIUM:
66         return CAIRO_HINT_STYLE_MEDIUM;
67     case FC_HINT_FULL:
68         return CAIRO_HINT_STYLE_FULL;
69     }
70     return CAIRO_HINT_STYLE_NONE;
71 }
72 
setCairoFontOptionsFromFontConfigPattern(cairo_font_options_t * options,FcPattern * pattern)73 void setCairoFontOptionsFromFontConfigPattern(cairo_font_options_t* options, FcPattern* pattern)
74 {
75     FcBool booleanResult;
76     int integerResult;
77 
78     if (FcPatternGetInteger(pattern, FC_RGBA, 0, &integerResult) == FcResultMatch) {
79         cairo_font_options_set_subpixel_order(options, convertFontConfigSubpixelOrder(integerResult));
80 
81         // Based on the logic in cairo-ft-font.c in the cairo source, a font with
82         // a subpixel order implies that is uses subpixel antialiasing.
83         if (integerResult != FC_RGBA_NONE)
84             cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_SUBPIXEL);
85     }
86 
87     if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &booleanResult) == FcResultMatch) {
88         // Only override the anti-aliasing setting if was previously turned off. Otherwise
89         // we'll override the preference which decides between gray anti-aliasing and
90         // subpixel anti-aliasing.
91         if (!booleanResult)
92             cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_NONE);
93         else if (cairo_font_options_get_antialias(options) == CAIRO_ANTIALIAS_NONE)
94             cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY);
95     }
96 
97     if (FcPatternGetInteger(pattern, FC_HINT_STYLE, 0, &integerResult) == FcResultMatch)
98         cairo_font_options_set_hint_style(options, convertFontConfigHintStyle(integerResult));
99     if (FcPatternGetBool(pattern, FC_HINTING, 0, &booleanResult) == FcResultMatch && !booleanResult)
100         cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);
101 }
102 
getDefaultFontOptions()103 static cairo_font_options_t* getDefaultFontOptions()
104 {
105 #if PLATFORM(GTK)
106     if (GdkScreen* screen = gdk_screen_get_default()) {
107         const cairo_font_options_t* screenOptions = gdk_screen_get_font_options(screen);
108         if (screenOptions)
109             return cairo_font_options_copy(screenOptions);
110     }
111 #endif
112     return cairo_font_options_create();
113 }
114 
FontPlatformData(FcPattern * pattern,const FontDescription & fontDescription)115 FontPlatformData::FontPlatformData(FcPattern* pattern, const FontDescription& fontDescription)
116     : m_pattern(pattern)
117     , m_fallbacks(0)
118     , m_size(fontDescription.computedPixelSize())
119     , m_syntheticBold(false)
120     , m_syntheticOblique(false)
121     , m_fixedWidth(false)
122     , m_scaledFont(0)
123 {
124     RefPtr<cairo_font_face_t> fontFace = adoptRef(cairo_ft_font_face_create_for_pattern(m_pattern.get()));
125     initializeWithFontFace(fontFace.get());
126 
127     int spacing;
128     if (FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing) == FcResultMatch && spacing == FC_MONO)
129         m_fixedWidth = true;
130 
131     if (fontDescription.weight() >= FontWeightBold) {
132         // The FC_EMBOLDEN property instructs us to fake the boldness of the font.
133         FcBool fontConfigEmbolden;
134         if (FcPatternGetBool(pattern, FC_EMBOLDEN, 0, &fontConfigEmbolden) == FcResultMatch)
135             m_syntheticBold = fontConfigEmbolden;
136     }
137 }
138 
FontPlatformData(float size,bool bold,bool italic)139 FontPlatformData::FontPlatformData(float size, bool bold, bool italic)
140     : m_fallbacks(0)
141     , m_size(size)
142     , m_syntheticBold(bold)
143     , m_syntheticOblique(italic)
144     , m_fixedWidth(false)
145     , m_scaledFont(0)
146 {
147     // We cannot create a scaled font here.
148 }
149 
FontPlatformData(cairo_font_face_t * fontFace,float size,bool bold,bool italic)150 FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, float size, bool bold, bool italic)
151     : m_fallbacks(0)
152     , m_size(size)
153     , m_syntheticBold(bold)
154     , m_syntheticOblique(italic)
155     , m_scaledFont(0)
156 {
157     initializeWithFontFace(fontFace);
158 
159     FT_Face fontConfigFace = cairo_ft_scaled_font_lock_face(m_scaledFont);
160     if (fontConfigFace) {
161         m_fixedWidth = fontConfigFace->face_flags & FT_FACE_FLAG_FIXED_WIDTH;
162         cairo_ft_scaled_font_unlock_face(m_scaledFont);
163     }
164 }
165 
operator =(const FontPlatformData & other)166 FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other)
167 {
168     // Check for self-assignment.
169     if (this == &other)
170         return *this;
171 
172     m_size = other.m_size;
173     m_syntheticBold = other.m_syntheticBold;
174     m_syntheticOblique = other.m_syntheticOblique;
175     m_fixedWidth = other.m_fixedWidth;
176     m_pattern = other.m_pattern;
177 
178     if (m_fallbacks) {
179         FcFontSetDestroy(m_fallbacks);
180         // This will be re-created on demand.
181         m_fallbacks = 0;
182     }
183 
184     if (m_scaledFont && m_scaledFont != hashTableDeletedFontValue())
185         cairo_scaled_font_destroy(m_scaledFont);
186     m_scaledFont = cairo_scaled_font_reference(other.m_scaledFont);
187 
188     return *this;
189 }
190 
FontPlatformData(const FontPlatformData & other)191 FontPlatformData::FontPlatformData(const FontPlatformData& other)
192     : m_fallbacks(0)
193     , m_scaledFont(0)
194 {
195     *this = other;
196 }
197 
FontPlatformData(const FontPlatformData & other,float size)198 FontPlatformData::FontPlatformData(const FontPlatformData& other, float size)
199 {
200     *this = other;
201 
202     // We need to reinitialize the instance, because the difference in size
203     // necessitates a new scaled font instance.
204     m_size = size;
205     initializeWithFontFace(cairo_scaled_font_get_font_face(m_scaledFont));
206 }
207 
~FontPlatformData()208 FontPlatformData::~FontPlatformData()
209 {
210     if (m_fallbacks) {
211         FcFontSetDestroy(m_fallbacks);
212         m_fallbacks = 0;
213     }
214 
215     if (m_scaledFont && m_scaledFont != hashTableDeletedFontValue())
216         cairo_scaled_font_destroy(m_scaledFont);
217 }
218 
isFixedPitch()219 bool FontPlatformData::isFixedPitch()
220 {
221     return m_fixedWidth;
222 }
223 
operator ==(const FontPlatformData & other) const224 bool FontPlatformData::operator==(const FontPlatformData& other) const
225 {
226     if (m_pattern == other.m_pattern)
227         return true;
228     if (!m_pattern || !other.m_pattern)
229         return false;
230     return FcPatternEqual(m_pattern.get(), other.m_pattern.get())
231         && m_scaledFont == other.m_scaledFont && m_size == other.m_size
232         && m_syntheticOblique == other.m_syntheticOblique && m_syntheticBold == other.m_syntheticBold;
233 }
234 
235 #ifndef NDEBUG
description() const236 String FontPlatformData::description() const
237 {
238     return String();
239 }
240 #endif
241 
initializeWithFontFace(cairo_font_face_t * fontFace)242 void FontPlatformData::initializeWithFontFace(cairo_font_face_t* fontFace)
243 {
244     cairo_font_options_t* options = getDefaultFontOptions();
245 
246     cairo_matrix_t ctm;
247     cairo_matrix_init_identity(&ctm);
248 
249     cairo_matrix_t fontMatrix;
250     if (!m_pattern)
251         cairo_matrix_init_scale(&fontMatrix, m_size, m_size);
252     else {
253         setCairoFontOptionsFromFontConfigPattern(options, m_pattern.get());
254 
255         // FontConfig may return a list of transformation matrices with the pattern, for instance,
256         // for fonts that are oblique. We use that to initialize the cairo font matrix.
257         FcMatrix fontConfigMatrix, *tempFontConfigMatrix;
258         FcMatrixInit(&fontConfigMatrix);
259 
260         // These matrices may be stacked in the pattern, so it's our job to get them all and multiply them.
261         for (int i = 0; FcPatternGetMatrix(m_pattern.get(), FC_MATRIX, i, &tempFontConfigMatrix) == FcResultMatch; i++)
262             FcMatrixMultiply(&fontConfigMatrix, &fontConfigMatrix, tempFontConfigMatrix);
263         cairo_matrix_init(&fontMatrix, fontConfigMatrix.xx, -fontConfigMatrix.yx,
264                           -fontConfigMatrix.xy, fontConfigMatrix.yy, 0, 0);
265 
266         // The matrix from FontConfig does not include the scale.
267         cairo_matrix_scale(&fontMatrix, m_size, m_size);
268     }
269 
270     m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options);
271     cairo_font_options_destroy(options);
272 }
273 
hasCompatibleCharmap()274 bool FontPlatformData::hasCompatibleCharmap()
275 {
276     if (!m_scaledFont)
277         return false;
278 
279     FT_Face freeTypeFace = cairo_ft_scaled_font_lock_face(m_scaledFont);
280     bool hasCompatibleCharmap = !(FT_Select_Charmap(freeTypeFace, ft_encoding_unicode)
281                                 && FT_Select_Charmap(freeTypeFace, ft_encoding_symbol)
282                                 && FT_Select_Charmap(freeTypeFace, ft_encoding_apple_roman));
283     cairo_ft_scaled_font_unlock_face(m_scaledFont);
284     return hasCompatibleCharmap;
285 }
286 
287 }
288