1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 4 * (C) 2000 Dirk Mueller (mueller@kde.org) 5 * Copyright (C) 2003, 2006, 2010, 2011 Apple Inc. All rights reserved. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 * 22 */ 23 24#include "config.h" 25#include "Font.h" 26 27#include "FloatRect.h" 28#include "FontCache.h" 29#include "FontTranscoder.h" 30#include "IntPoint.h" 31#include "GlyphBuffer.h" 32#include "TextRun.h" 33#include "WidthIterator.h" 34#include <wtf/MathExtras.h> 35#include <wtf/UnusedParam.h> 36 37using namespace WTF; 38using namespace Unicode; 39 40namespace WebCore { 41 42Font::CodePath Font::s_codePath = Auto; 43 44// ============================================================================================ 45// Font Implementation (Cross-Platform Portion) 46// ============================================================================================ 47 48Font::Font() 49 : m_letterSpacing(0) 50 , m_wordSpacing(0) 51 , m_isPlatformFont(false) 52 , m_needsTranscoding(false) 53{ 54} 55 56Font::Font(const FontDescription& fd, short letterSpacing, short wordSpacing) 57 : m_fontDescription(fd) 58 , m_letterSpacing(letterSpacing) 59 , m_wordSpacing(wordSpacing) 60 , m_isPlatformFont(false) 61 , m_needsTranscoding(fontTranscoder().needsTranscoding(fd)) 62{ 63} 64 65Font::Font(const FontPlatformData& fontData, bool isPrinterFont, FontSmoothingMode fontSmoothingMode) 66 : m_fontList(FontFallbackList::create()) 67 , m_letterSpacing(0) 68 , m_wordSpacing(0) 69 , m_isPlatformFont(true) 70{ 71 m_fontDescription.setUsePrinterFont(isPrinterFont); 72 m_fontDescription.setFontSmoothing(fontSmoothingMode); 73 m_needsTranscoding = fontTranscoder().needsTranscoding(fontDescription()); 74 m_fontList->setPlatformFont(fontData); 75} 76 77Font::Font(const Font& other) 78 : m_fontDescription(other.m_fontDescription) 79 , m_fontList(other.m_fontList) 80 , m_letterSpacing(other.m_letterSpacing) 81 , m_wordSpacing(other.m_wordSpacing) 82 , m_isPlatformFont(other.m_isPlatformFont) 83 , m_needsTranscoding(fontTranscoder().needsTranscoding(other.m_fontDescription)) 84{ 85} 86 87Font& Font::operator=(const Font& other) 88{ 89 m_fontDescription = other.m_fontDescription; 90 m_fontList = other.m_fontList; 91 m_letterSpacing = other.m_letterSpacing; 92 m_wordSpacing = other.m_wordSpacing; 93 m_isPlatformFont = other.m_isPlatformFont; 94 m_needsTranscoding = other.m_needsTranscoding; 95 return *this; 96} 97 98bool Font::operator==(const Font& other) const 99{ 100 // Our FontData don't have to be checked, since checking the font description will be fine. 101 // FIXME: This does not work if the font was made with the FontPlatformData constructor. 102 if (loadingCustomFonts() || other.loadingCustomFonts()) 103 return false; 104 105 FontSelector* first = m_fontList ? m_fontList->fontSelector() : 0; 106 FontSelector* second = other.m_fontList ? other.m_fontList->fontSelector() : 0; 107 108 return first == second 109 && m_fontDescription == other.m_fontDescription 110 && m_letterSpacing == other.m_letterSpacing 111 && m_wordSpacing == other.m_wordSpacing 112 && (m_fontList ? m_fontList->generation() : 0) == (other.m_fontList ? other.m_fontList->generation() : 0); 113} 114 115void Font::update(PassRefPtr<FontSelector> fontSelector) const 116{ 117 // FIXME: It is pretty crazy that we are willing to just poke into a RefPtr, but it ends up 118 // being reasonably safe (because inherited fonts in the render tree pick up the new 119 // style anyway. Other copies are transient, e.g., the state in the GraphicsContext, and 120 // won't stick around long enough to get you in trouble). Still, this is pretty disgusting, 121 // and could eventually be rectified by using RefPtrs for Fonts themselves. 122 if (!m_fontList) 123 m_fontList = FontFallbackList::create(); 124 m_fontList->invalidate(fontSelector); 125} 126 127void Font::drawText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const 128{ 129 // Don't draw anything while we are using custom fonts that are in the process of loading. 130 if (loadingCustomFonts()) 131 return; 132 133 to = (to == -1 ? run.length() : to); 134 135#if ENABLE(SVG_FONTS) 136 if (primaryFont()->isSVGFont()) { 137 drawTextUsingSVGFont(context, run, point, from, to); 138 return; 139 } 140#endif 141 142 if (codePath(run) != Complex) 143 return drawSimpleText(context, run, point, from, to); 144 145 return drawComplexText(context, run, point, from, to); 146} 147 148void Font::drawEmphasisMarks(GraphicsContext* context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) const 149{ 150 if (loadingCustomFonts()) 151 return; 152 153 if (to < 0) 154 to = run.length(); 155 156#if ENABLE(SVG_FONTS) 157 // FIXME: Implement for SVG fonts. 158 if (primaryFont()->isSVGFont()) 159 return; 160#endif 161 162 if (codePath(run) != Complex) 163 drawEmphasisMarksForSimpleText(context, run, mark, point, from, to); 164 else 165 drawEmphasisMarksForComplexText(context, run, mark, point, from, to); 166} 167 168float Font::width(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const 169{ 170#if ENABLE(SVG_FONTS) 171 if (primaryFont()->isSVGFont()) 172 return floatWidthUsingSVGFont(run); 173#endif 174 175 CodePath codePathToUse = codePath(run); 176 if (codePathToUse != Complex) { 177 // If the complex text implementation cannot return fallback fonts, avoid 178 // returning them for simple text as well. 179 static bool returnFallbackFonts = canReturnFallbackFontsForComplexText(); 180 return floatWidthForSimpleText(run, 0, returnFallbackFonts ? fallbackFonts : 0, codePathToUse == SimpleWithGlyphOverflow || (glyphOverflow && glyphOverflow->computeBounds) ? glyphOverflow : 0); 181 } 182 183 return floatWidthForComplexText(run, fallbackFonts, glyphOverflow); 184} 185 186float Font::width(const TextRun& run, int extraCharsAvailable, int& charsConsumed, String& glyphName) const 187{ 188#if !ENABLE(SVG_FONTS) 189 UNUSED_PARAM(extraCharsAvailable); 190#else 191 if (primaryFont()->isSVGFont()) 192 return floatWidthUsingSVGFont(run, extraCharsAvailable, charsConsumed, glyphName); 193#endif 194 195 charsConsumed = run.length(); 196 glyphName = ""; 197 198 if (codePath(run) != Complex) 199 return floatWidthForSimpleText(run, 0); 200 201 return floatWidthForComplexText(run); 202} 203 204FloatRect Font::selectionRectForText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const 205{ 206#if ENABLE(SVG_FONTS) 207 if (primaryFont()->isSVGFont()) 208 return selectionRectForTextUsingSVGFont(run, point, h, from, to); 209#endif 210 211 to = (to == -1 ? run.length() : to); 212 213 if (codePath(run) != Complex) 214 return selectionRectForSimpleText(run, point, h, from, to); 215 216 return selectionRectForComplexText(run, point, h, from, to); 217} 218 219int Font::offsetForPosition(const TextRun& run, float x, bool includePartialGlyphs) const 220{ 221#if ENABLE(SVG_FONTS) 222 if (primaryFont()->isSVGFont()) 223 return offsetForPositionForTextUsingSVGFont(run, x, includePartialGlyphs); 224#endif 225 226 if (codePath(run) != Complex) 227 return offsetForPositionForSimpleText(run, x, includePartialGlyphs); 228 229 return offsetForPositionForComplexText(run, x, includePartialGlyphs); 230} 231 232#if ENABLE(SVG_FONTS) 233bool Font::isSVGFont() const 234{ 235 return primaryFont()->isSVGFont(); 236} 237#endif 238 239String Font::normalizeSpaces(const UChar* characters, unsigned length) 240{ 241 UChar* buffer; 242 String normalized = String::createUninitialized(length, buffer); 243 244 for (unsigned i = 0; i < length; ++i) 245 buffer[i] = normalizeSpaces(characters[i]); 246 247 return normalized; 248} 249 250static bool shouldUseFontSmoothing = true; 251 252void Font::setShouldUseSmoothing(bool shouldUseSmoothing) 253{ 254 ASSERT(isMainThread()); 255 shouldUseFontSmoothing = shouldUseSmoothing; 256} 257 258bool Font::shouldUseSmoothing() 259{ 260 return shouldUseFontSmoothing; 261} 262 263void Font::setCodePath(CodePath p) 264{ 265 s_codePath = p; 266} 267 268Font::CodePath Font::codePath() 269{ 270 return s_codePath; 271} 272 273Font::CodePath Font::codePath(const TextRun& run) const 274{ 275 if (s_codePath != Auto) 276 return s_codePath; 277 278#if PLATFORM(QT) 279 if (run.expansion() || run.rtl() || isSmallCaps() || wordSpacing() || letterSpacing()) 280 return Complex; 281#endif 282 283 CodePath result = Simple; 284 285 // Start from 0 since drawing and highlighting also measure the characters before run->from 286 for (int i = 0; i < run.length(); i++) { 287 const UChar c = run[i]; 288 if (c < 0x300) // U+0300 through U+036F Combining diacritical marks 289 continue; 290 if (c <= 0x36F) 291 return Complex; 292 293 if (c < 0x0591 || c == 0x05BE) // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha 294 continue; 295 if (c <= 0x05CF) 296 return Complex; 297 298 if (c < 0x0600) // U+0600 through U+1059 Arabic, Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar 299 continue; 300 if (c <= 0x1059) 301 return Complex; 302 303 if (c < 0x1100) // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left here if you precompose; Modern Korean will be precomposed as a result of step A) 304 continue; 305 if (c <= 0x11FF) 306 return Complex; 307 308 if (c < 0x1780) // U+1780 through U+18AF Khmer, Mongolian 309 continue; 310 if (c <= 0x18AF) 311 return Complex; 312 313 if (c < 0x1900) // U+1900 through U+194F Limbu (Unicode 4.0) 314 continue; 315 if (c <= 0x194F) 316 return Complex; 317 318 if (c < 0x1E00) // U+1E00 through U+2000 characters with diacritics and stacked diacritics 319 continue; 320 if (c <= 0x2000) { 321 result = SimpleWithGlyphOverflow; 322 continue; 323 } 324 325 if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols 326 continue; 327 if (c <= 0x20FF) 328 return Complex; 329 330 if (c < 0xFE20) // U+FE20 through U+FE2F Combining half marks 331 continue; 332 if (c <= 0xFE2F) 333 return Complex; 334 } 335 336 if (typesettingFeatures()) 337 return Complex; 338 339 return result; 340} 341 342bool Font::isCJKIdeograph(UChar32 c) 343{ 344 // The basic CJK Unified Ideographs block. 345 if (c >= 0x4E00 && c <= 0x9FFF) 346 return true; 347 348 // CJK Unified Ideographs Extension A. 349 if (c >= 0x3400 && c <= 0x4DBF) 350 return true; 351 352 // CJK Radicals Supplement. 353 if (c >= 0x2E80 && c <= 0x2EFF) 354 return true; 355 356 // Kangxi Radicals. 357 if (c >= 0x2F00 && c <= 0x2FDF) 358 return true; 359 360 // CJK Strokes. 361 if (c >= 0x31C0 && c <= 0x31EF) 362 return true; 363 364 // CJK Compatibility Ideographs. 365 if (c >= 0xF900 && c <= 0xFAFF) 366 return true; 367 368 // CJK Unified Ideographs Extension B. 369 if (c >= 0x20000 && c <= 0x2A6DF) 370 return true; 371 372 // CJK Unified Ideographs Extension C. 373 if (c >= 0x2A700 && c <= 0x2B73F) 374 return true; 375 376 // CJK Unified Ideographs Extension D. 377 if (c >= 0x2B740 && c <= 0x2B81F) 378 return true; 379 380 // CJK Compatibility Ideographs Supplement. 381 if (c >= 0x2F800 && c <= 0x2FA1F) 382 return true; 383 384 return false; 385} 386 387bool Font::isCJKIdeographOrSymbol(UChar32 c) 388{ 389 // 0x2C7 Caron, Mandarin Chinese 3rd Tone 390 // 0x2CA Modifier Letter Acute Accent, Mandarin Chinese 2nd Tone 391 // 0x2CB Modifier Letter Grave Access, Mandarin Chinese 4th Tone 392 // 0x2D9 Dot Above, Mandarin Chinese 5th Tone 393 if ((c == 0x2C7) || (c == 0x2CA) || (c == 0x2CB) || (c == 0x2D9)) 394 return true; 395 396 // Ideographic Description Characters. 397 if (c >= 0x2FF0 && c <= 0x2FFF) 398 return true; 399 400 // CJK Symbols and Punctuation. 401 if (c >= 0x3000 && c <= 0x303F) 402 return true; 403 404 // Hiragana 405 if (c >= 0x3040 && c <= 0x309F) 406 return true; 407 408 // Katakana 409 if (c >= 0x30A0 && c <= 0x30FF) 410 return true; 411 412 // Bopomofo 413 if (c >= 0x3100 && c <= 0x312F) 414 return true; 415 416 // Bopomofo Extended 417 if (c >= 0x31A0 && c <= 0x31BF) 418 return true; 419 420 // Enclosed CJK Letters and Months. 421 if (c >= 0x3200 && c <= 0x32FF) 422 return true; 423 424 // CJK Compatibility. 425 if (c >= 0x3300 && c <= 0x33FF) 426 return true; 427 428 // CJK Compatibility Forms. 429 if (c >= 0xFE30 && c <= 0xFE4F) 430 return true; 431 432 // Halfwidth and Fullwidth Forms 433 // Usually only used in CJK 434 if (c >= 0xFF00 && c <= 0xFFEF) 435 return true; 436 437 // Emoji. 438 if (c >= 0x1F200 && c <= 0x1F6F) 439 return true; 440 441 return isCJKIdeograph(c); 442} 443 444unsigned Font::expansionOpportunityCount(const UChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion) 445{ 446 static bool expandAroundIdeographs = canExpandAroundIdeographsInComplexText(); 447 unsigned count = 0; 448 if (direction == LTR) { 449 for (size_t i = 0; i < length; ++i) { 450 UChar32 character = characters[i]; 451 if (treatAsSpace(character)) { 452 count++; 453 isAfterExpansion = true; 454 continue; 455 } 456 if (U16_IS_LEAD(character) && i + 1 < length && U16_IS_TRAIL(characters[i + 1])) { 457 character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]); 458 i++; 459 } 460 if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { 461 if (!isAfterExpansion) 462 count++; 463 count++; 464 isAfterExpansion = true; 465 continue; 466 } 467 isAfterExpansion = false; 468 } 469 } else { 470 for (size_t i = length; i > 0; --i) { 471 UChar32 character = characters[i - 1]; 472 if (treatAsSpace(character)) { 473 count++; 474 isAfterExpansion = true; 475 continue; 476 } 477 if (U16_IS_TRAIL(character) && i > 1 && U16_IS_LEAD(characters[i - 2])) { 478 character = U16_GET_SUPPLEMENTARY(characters[i - 2], character); 479 i--; 480 } 481 if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { 482 if (!isAfterExpansion) 483 count++; 484 count++; 485 isAfterExpansion = true; 486 continue; 487 } 488 isAfterExpansion = false; 489 } 490 } 491 return count; 492} 493 494bool Font::canReceiveTextEmphasis(UChar32 c) 495{ 496 CharCategory category = Unicode::category(c); 497 if (category & (Separator_Space | Separator_Line | Separator_Paragraph | Other_NotAssigned | Other_Control | Other_Format)) 498 return false; 499 500 // Additional word-separator characters listed in CSS Text Level 3 Editor's Draft 3 November 2010. 501 if (c == ethiopicWordspace || c == aegeanWordSeparatorLine || c == aegeanWordSeparatorDot 502 || c == ugariticWordDivider || c == tibetanMarkIntersyllabicTsheg || c == tibetanMarkDelimiterTshegBstar) 503 return false; 504 505 return true; 506} 507 508} 509