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