| OLD | NEW |
| (Empty) |
| 1 /** | |
| 2 * This file is part of the html renderer for KDE. | |
| 3 * | |
| 4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | |
| 5 * (C) 1999 Antti Koivisto (koivisto@kde.org) | |
| 6 * (C) 2000 Dirk Mueller (mueller@kde.org) | |
| 7 * Copyright (C) 2003, 2006 Apple Computer, Inc. | |
| 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 | |
| 26 #include "config.h" | |
| 27 #include "Font.h" | |
| 28 | |
| 29 #include "CharacterNames.h" | |
| 30 #include "FloatRect.h" | |
| 31 #include "FontCache.h" | |
| 32 #include "FontFallbackList.h" | |
| 33 #include "IntPoint.h" | |
| 34 #include "GlyphBuffer.h" | |
| 35 #include <wtf/unicode/Unicode.h> | |
| 36 #include <wtf/MathExtras.h> | |
| 37 | |
| 38 #if USE(ICU_UNICODE) | |
| 39 #include <unicode/unorm.h> | |
| 40 #endif | |
| 41 | |
| 42 using namespace WTF; | |
| 43 using namespace Unicode; | |
| 44 | |
| 45 namespace WebCore { | |
| 46 | |
| 47 // According to http://www.unicode.org/Public/UNIDATA/UCD.html#Canonical_Combini
ng_Class_Values | |
| 48 const uint8_t hiraganaKatakanaVoicingMarksCombiningClass = 8; | |
| 49 | |
| 50 const uint8_t Font::gRoundingHackCharacterTable[256] = { | |
| 51 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*\t*/, 1 /*\n*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 52 1 /*space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*-*/, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*?*/, | |
| 53 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0, | |
| 54 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0, | |
| 55 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0, | |
| 56 1 /*no-break space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 57 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0, | |
| 58 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0 | |
| 59 }; | |
| 60 | |
| 61 Font::CodePath Font::codePath = Auto; | |
| 62 | |
| 63 struct WidthIterator { | |
| 64 WidthIterator(const Font* font, const TextRun& run); | |
| 65 | |
| 66 void advance(int to, GlyphBuffer* glyphBuffer = 0); | |
| 67 bool advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer = 0); | |
| 68 | |
| 69 const Font* m_font; | |
| 70 | |
| 71 const TextRun& m_run; | |
| 72 int m_end; | |
| 73 | |
| 74 unsigned m_currentCharacter; | |
| 75 float m_runWidthSoFar; | |
| 76 float m_padding; | |
| 77 float m_padPerSpace; | |
| 78 float m_finalRoundingWidth; | |
| 79 | |
| 80 private: | |
| 81 UChar32 normalizeVoicingMarks(int currentCharacter); | |
| 82 }; | |
| 83 | |
| 84 WidthIterator::WidthIterator(const Font* font, const TextRun& run) | |
| 85 : m_font(font) | |
| 86 , m_run(run) | |
| 87 , m_end(run.length()) | |
| 88 , m_currentCharacter(0) | |
| 89 , m_runWidthSoFar(0) | |
| 90 , m_finalRoundingWidth(0) | |
| 91 { | |
| 92 // If the padding is non-zero, count the number of spaces in the run | |
| 93 // and divide that by the padding for per space addition. | |
| 94 m_padding = m_run.padding(); | |
| 95 if (!m_padding) | |
| 96 m_padPerSpace = 0; | |
| 97 else { | |
| 98 float numSpaces = 0; | |
| 99 for (int i = 0; i < run.length(); i++) | |
| 100 if (Font::treatAsSpace(m_run[i])) | |
| 101 numSpaces++; | |
| 102 | |
| 103 if (numSpaces == 0) | |
| 104 m_padPerSpace = 0; | |
| 105 else | |
| 106 m_padPerSpace = ceilf(m_run.padding() / numSpaces); | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) | |
| 111 { | |
| 112 if (offset > m_end) | |
| 113 offset = m_end; | |
| 114 | |
| 115 int currentCharacter = m_currentCharacter; | |
| 116 const UChar* cp = m_run.data(currentCharacter); | |
| 117 | |
| 118 bool rtl = m_run.rtl(); | |
| 119 bool hasExtraSpacing = m_font->letterSpacing() || m_font->wordSpacing() || m
_padding; | |
| 120 | |
| 121 float runWidthSoFar = m_runWidthSoFar; | |
| 122 float lastRoundingWidth = m_finalRoundingWidth; | |
| 123 | |
| 124 while (currentCharacter < offset) { | |
| 125 UChar32 c = *cp; | |
| 126 unsigned clusterLength = 1; | |
| 127 if (c >= 0x3041) { | |
| 128 if (c <= 0x30FE) { | |
| 129 // Deal with Hiragana and Katakana voiced and semi-voiced syllab
les. | |
| 130 // Normalize into composed form, and then look for glyph with ba
se + combined mark. | |
| 131 // Check above for character range to minimize performance impac
t. | |
| 132 UChar32 normalized = normalizeVoicingMarks(currentCharacter); | |
| 133 if (normalized) { | |
| 134 c = normalized; | |
| 135 clusterLength = 2; | |
| 136 } | |
| 137 } else if (U16_IS_SURROGATE(c)) { | |
| 138 if (!U16_IS_SURROGATE_LEAD(c)) | |
| 139 break; | |
| 140 | |
| 141 // Do we have a surrogate pair? If so, determine the full Unico
de (32 bit) | |
| 142 // code point before glyph lookup. | |
| 143 // Make sure we have another character and it's a low surrogate. | |
| 144 if (currentCharacter + 1 >= m_run.length()) | |
| 145 break; | |
| 146 UChar low = cp[1]; | |
| 147 if (!U16_IS_TRAIL(low)) | |
| 148 break; | |
| 149 c = U16_GET_SUPPLEMENTARY(c, low); | |
| 150 clusterLength = 2; | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 const GlyphData& glyphData = m_font->glyphDataForCharacter(c, rtl); | |
| 155 Glyph glyph = glyphData.glyph; | |
| 156 const SimpleFontData* fontData = glyphData.fontData; | |
| 157 | |
| 158 ASSERT(fontData); | |
| 159 | |
| 160 // Now that we have a glyph and font data, get its width. | |
| 161 float width; | |
| 162 if (c == '\t' && m_run.allowTabs()) { | |
| 163 float tabWidth = m_font->tabWidth(); | |
| 164 width = tabWidth - fmodf(m_run.xPos() + runWidthSoFar, tabWidth); | |
| 165 } else { | |
| 166 width = fontData->widthForGlyph(glyph); | |
| 167 // We special case spaces in two ways when applying word rounding. | |
| 168 // First, we round spaces to an adjusted width in all fonts. | |
| 169 // Second, in fixed-pitch fonts we ensure that all characters that | |
| 170 // match the width of the space character have the same width as the
space character. | |
| 171 if (width == fontData->m_spaceWidth && (fontData->m_treatAsFixedPitc
h || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding()) | |
| 172 width = fontData->m_adjustedSpaceWidth; | |
| 173 } | |
| 174 | |
| 175 if (hasExtraSpacing && !m_run.spacingDisabled()) { | |
| 176 // Account for letter-spacing. | |
| 177 if (width && m_font->letterSpacing()) | |
| 178 width += m_font->letterSpacing(); | |
| 179 | |
| 180 if (Font::treatAsSpace(c)) { | |
| 181 // Account for padding. WebCore uses space padding to justify te
xt. | |
| 182 // We distribute the specified padding over the available spaces
in the run. | |
| 183 if (m_padding) { | |
| 184 // Use left over padding if not evenly divisible by number o
f spaces. | |
| 185 if (m_padding < m_padPerSpace) { | |
| 186 width += m_padding; | |
| 187 m_padding = 0; | |
| 188 } else { | |
| 189 width += m_padPerSpace; | |
| 190 m_padding -= m_padPerSpace; | |
| 191 } | |
| 192 } | |
| 193 | |
| 194 // Account for word spacing. | |
| 195 // We apply additional space between "words" by adding width to
the space character. | |
| 196 if (currentCharacter != 0 && !Font::treatAsSpace(cp[-1]) && m_fo
nt->wordSpacing()) | |
| 197 width += m_font->wordSpacing(); | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 // Advance past the character we just dealt with. | |
| 202 cp += clusterLength; | |
| 203 currentCharacter += clusterLength; | |
| 204 | |
| 205 // Account for float/integer impedance mismatch between CG and KHTML. "W
ords" (characters | |
| 206 // followed by a character defined by isRoundingHackCharacter()) are alw
ays an integer width. | |
| 207 // We adjust the width of the last character of a "word" to ensure an in
teger width. | |
| 208 // If we move KHTML to floats we can remove this (and related) hacks. | |
| 209 | |
| 210 float oldWidth = width; | |
| 211 | |
| 212 // Force characters that are used to determine word boundaries for the r
ounding hack | |
| 213 // to be integer width, so following words will start on an integer boun
dary. | |
| 214 if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(c)) | |
| 215 width = ceilf(width); | |
| 216 | |
| 217 // Check to see if the next character is a "rounding hack character", if
so, adjust | |
| 218 // width so that the total run width will be on an integer boundary. | |
| 219 if ((m_run.applyWordRounding() && currentCharacter < m_run.length() && F
ont::isRoundingHackCharacter(*cp)) | |
| 220 || (m_run.applyRunRounding() && currentCharacter >= m_end)) { | |
| 221 float totalWidth = runWidthSoFar + width; | |
| 222 width += ceilf(totalWidth) - totalWidth; | |
| 223 } | |
| 224 | |
| 225 runWidthSoFar += width; | |
| 226 | |
| 227 if (glyphBuffer) | |
| 228 glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidt
h : width)); | |
| 229 | |
| 230 lastRoundingWidth = width - oldWidth; | |
| 231 } | |
| 232 | |
| 233 m_currentCharacter = currentCharacter; | |
| 234 m_runWidthSoFar = runWidthSoFar; | |
| 235 m_finalRoundingWidth = lastRoundingWidth; | |
| 236 } | |
| 237 | |
| 238 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer) | |
| 239 { | |
| 240 glyphBuffer->clear(); | |
| 241 advance(m_currentCharacter + 1, glyphBuffer); | |
| 242 float w = 0; | |
| 243 for (int i = 0; i < glyphBuffer->size(); ++i) | |
| 244 w += glyphBuffer->advanceAt(i); | |
| 245 width = w; | |
| 246 return !glyphBuffer->isEmpty(); | |
| 247 } | |
| 248 | |
| 249 UChar32 WidthIterator::normalizeVoicingMarks(int currentCharacter) | |
| 250 { | |
| 251 if (currentCharacter + 1 < m_end) { | |
| 252 if (combiningClass(m_run[currentCharacter + 1]) == hiraganaKatakanaVoici
ngMarksCombiningClass) { | |
| 253 #if USE(ICU_UNICODE) | |
| 254 // Normalize into composed form using 3.2 rules. | |
| 255 UChar normalizedCharacters[2] = { 0, 0 }; | |
| 256 UErrorCode uStatus = U_ZERO_ERROR; | |
| 257 int32_t resultLength = unorm_normalize(m_run.data(currentCharacter),
2, | |
| 258 UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], 2, &uSta
tus); | |
| 259 if (resultLength == 1 && uStatus == 0) | |
| 260 return normalizedCharacters[0]; | |
| 261 #elif USE(QT4_UNICODE) | |
| 262 QString tmp(reinterpret_cast<const QChar*>(m_run.data(currentCharact
er)), 2); | |
| 263 QString res = tmp.normalized(QString::NormalizationForm_C, QChar::Un
icode_3_2); | |
| 264 if (res.length() == 1) | |
| 265 return res.at(0).unicode(); | |
| 266 #endif | |
| 267 } | |
| 268 } | |
| 269 return 0; | |
| 270 } | |
| 271 | |
| 272 // =============================================================================
=============== | |
| 273 // Font Implementation (Cross-Platform Portion) | |
| 274 // =============================================================================
=============== | |
| 275 | |
| 276 Font::Font() | |
| 277 : m_pageZero(0) | |
| 278 , m_cachedPrimaryFont(0) | |
| 279 , m_letterSpacing(0) | |
| 280 , m_wordSpacing(0) | |
| 281 , m_isPlatformFont(false) | |
| 282 { | |
| 283 } | |
| 284 | |
| 285 Font::Font(const FontDescription& fd, short letterSpacing, short wordSpacing) | |
| 286 : m_fontDescription(fd) | |
| 287 , m_pageZero(0) | |
| 288 , m_cachedPrimaryFont(0) | |
| 289 , m_letterSpacing(letterSpacing) | |
| 290 , m_wordSpacing(wordSpacing) | |
| 291 , m_isPlatformFont(false) | |
| 292 { | |
| 293 } | |
| 294 | |
| 295 Font::Font(const FontPlatformData& fontData, bool isPrinterFont) | |
| 296 : m_fontList(FontFallbackList::create()) | |
| 297 , m_pageZero(0) | |
| 298 , m_cachedPrimaryFont(0) | |
| 299 , m_letterSpacing(0) | |
| 300 , m_wordSpacing(0) | |
| 301 , m_isPlatformFont(true) | |
| 302 { | |
| 303 m_fontDescription.setUsePrinterFont(isPrinterFont); | |
| 304 m_fontList->setPlatformFont(fontData); | |
| 305 } | |
| 306 | |
| 307 Font::Font(const Font& other) | |
| 308 : m_fontDescription(other.m_fontDescription) | |
| 309 , m_fontList(other.m_fontList) | |
| 310 , m_pages(other.m_pages) | |
| 311 , m_pageZero(other.m_pageZero) | |
| 312 , m_cachedPrimaryFont(other.m_cachedPrimaryFont) | |
| 313 , m_letterSpacing(other.m_letterSpacing) | |
| 314 , m_wordSpacing(other.m_wordSpacing) | |
| 315 , m_isPlatformFont(other.m_isPlatformFont) | |
| 316 { | |
| 317 } | |
| 318 | |
| 319 Font& Font::operator=(const Font& other) | |
| 320 { | |
| 321 m_fontDescription = other.m_fontDescription; | |
| 322 m_fontList = other.m_fontList; | |
| 323 m_pages = other.m_pages; | |
| 324 m_pageZero = other.m_pageZero; | |
| 325 m_cachedPrimaryFont = other.m_cachedPrimaryFont; | |
| 326 m_letterSpacing = other.m_letterSpacing; | |
| 327 m_wordSpacing = other.m_wordSpacing; | |
| 328 m_isPlatformFont = other.m_isPlatformFont; | |
| 329 return *this; | |
| 330 } | |
| 331 | |
| 332 Font::~Font() | |
| 333 { | |
| 334 } | |
| 335 | |
| 336 bool Font::operator==(const Font& other) const | |
| 337 { | |
| 338 // Our FontData don't have to be checked, since checking the font descriptio
n will be fine. | |
| 339 // FIXME: This does not work if the font was made with the FontPlatformData
constructor. | |
| 340 if ((m_fontList && m_fontList->loadingCustomFonts()) || | |
| 341 (other.m_fontList && other.m_fontList->loadingCustomFonts())) | |
| 342 return false; | |
| 343 | |
| 344 FontSelector* first = m_fontList ? m_fontList->fontSelector() : 0; | |
| 345 FontSelector* second = other.m_fontList ? other.m_fontList->fontSelector() :
0; | |
| 346 | |
| 347 return first == second | |
| 348 && m_fontDescription == other.m_fontDescription | |
| 349 && m_letterSpacing == other.m_letterSpacing | |
| 350 && m_wordSpacing == other.m_wordSpacing | |
| 351 && (m_fontList ? m_fontList->generation() : 0) == (other.m_fontList ?
other.m_fontList->generation() : 0); | |
| 352 } | |
| 353 | |
| 354 const GlyphData& Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceS
mallCaps) const | |
| 355 { | |
| 356 bool useSmallCapsFont = forceSmallCaps; | |
| 357 if (m_fontDescription.smallCaps()) { | |
| 358 UChar32 upperC = Unicode::toUpper(c); | |
| 359 if (upperC != c) { | |
| 360 c = upperC; | |
| 361 useSmallCapsFont = true; | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 if (mirror) | |
| 366 c = mirroredChar(c); | |
| 367 | |
| 368 unsigned pageNumber = (c / GlyphPage::size); | |
| 369 | |
| 370 GlyphPageTreeNode* node = pageNumber ? m_pages.get(pageNumber) : m_pageZero; | |
| 371 if (!node) { | |
| 372 node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber); | |
| 373 if (pageNumber) | |
| 374 m_pages.set(pageNumber, node); | |
| 375 else | |
| 376 m_pageZero = node; | |
| 377 } | |
| 378 | |
| 379 GlyphPage* page; | |
| 380 if (!useSmallCapsFont) { | |
| 381 // Fastest loop, for the common case (not small caps). | |
| 382 while (true) { | |
| 383 page = node->page(); | |
| 384 if (page) { | |
| 385 const GlyphData& data = page->glyphDataForCharacter(c); | |
| 386 if (data.fontData) | |
| 387 return data; | |
| 388 if (node->isSystemFallback()) | |
| 389 break; | |
| 390 } | |
| 391 | |
| 392 // Proceed with the fallback list. | |
| 393 node = node->getChild(fontDataAt(node->level()), pageNumber); | |
| 394 if (pageNumber) | |
| 395 m_pages.set(pageNumber, node); | |
| 396 else | |
| 397 m_pageZero = node; | |
| 398 } | |
| 399 } else { | |
| 400 while (true) { | |
| 401 page = node->page(); | |
| 402 if (page) { | |
| 403 const GlyphData& data = page->glyphDataForCharacter(c); | |
| 404 if (data.fontData) { | |
| 405 // The smallCapsFontData function should not normally return
0. | |
| 406 // But if it does, we will just render the capital letter bi
g. | |
| 407 const SimpleFontData* smallCapsFontData = data.fontData->sma
llCapsFontData(m_fontDescription); | |
| 408 if (!smallCapsFontData) | |
| 409 return data; | |
| 410 | |
| 411 GlyphPageTreeNode* smallCapsNode = GlyphPageTreeNode::getRoo
tChild(smallCapsFontData, pageNumber); | |
| 412 const GlyphPage* smallCapsPage = smallCapsNode->page(); | |
| 413 if (smallCapsPage) { | |
| 414 const GlyphData& data = smallCapsPage->glyphDataForChara
cter(c); | |
| 415 if (data.fontData) | |
| 416 return data; | |
| 417 } | |
| 418 | |
| 419 // Do not attempt system fallback off the smallCapsFontData.
This is the very unlikely case that | |
| 420 // a font has the lowercase character but the small caps fon
t does not have its uppercase version. | |
| 421 return smallCapsFontData->missingGlyphData(); | |
| 422 } | |
| 423 | |
| 424 if (node->isSystemFallback()) | |
| 425 break; | |
| 426 } | |
| 427 | |
| 428 // Proceed with the fallback list. | |
| 429 node = node->getChild(fontDataAt(node->level()), pageNumber); | |
| 430 if (pageNumber) | |
| 431 m_pages.set(pageNumber, node); | |
| 432 else | |
| 433 m_pageZero = node; | |
| 434 } | |
| 435 } | |
| 436 | |
| 437 ASSERT(page); | |
| 438 ASSERT(node->isSystemFallback()); | |
| 439 | |
| 440 // System fallback is character-dependent. When we get here, we | |
| 441 // know that the character in question isn't in the system fallback | |
| 442 // font's glyph page. Try to lazily create it here. | |
| 443 UChar codeUnits[2]; | |
| 444 int codeUnitsLength; | |
| 445 if (c <= 0xFFFF) { | |
| 446 UChar c16 = c; | |
| 447 if (Font::treatAsSpace(c16)) | |
| 448 codeUnits[0] = ' '; | |
| 449 else if (Font::treatAsZeroWidthSpace(c16)) | |
| 450 codeUnits[0] = zeroWidthSpace; | |
| 451 else | |
| 452 codeUnits[0] = c16; | |
| 453 codeUnitsLength = 1; | |
| 454 } else { | |
| 455 codeUnits[0] = U16_LEAD(c); | |
| 456 codeUnits[1] = U16_TRAIL(c); | |
| 457 codeUnitsLength = 2; | |
| 458 } | |
| 459 const SimpleFontData* characterFontData = FontCache::getFontDataForCharacter
s(*this, codeUnits, codeUnitsLength); | |
| 460 if (useSmallCapsFont) | |
| 461 characterFontData = characterFontData->smallCapsFontData(m_fontDescripti
on); | |
| 462 if (characterFontData) { | |
| 463 // Got the fallback glyph and font. | |
| 464 GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontD
ata, pageNumber)->page(); | |
| 465 const GlyphData& data = fallbackPage && fallbackPage->glyphDataForCharac
ter(c).fontData ? fallbackPage->glyphDataForCharacter(c) : characterFontData->mi
ssingGlyphData(); | |
| 466 // Cache it so we don't have to do system fallback again next time. | |
| 467 if (!useSmallCapsFont) | |
| 468 page->setGlyphDataForCharacter(c, data.glyph, data.fontData); | |
| 469 return data; | |
| 470 } | |
| 471 | |
| 472 // Even system fallback can fail; use the missing glyph in that case. | |
| 473 // FIXME: It would be nicer to use the missing glyph from the last resort fo
nt instead. | |
| 474 const GlyphData& data = primaryFont()->missingGlyphData(); | |
| 475 if (!useSmallCapsFont) | |
| 476 page->setGlyphDataForCharacter(c, data.glyph, data.fontData); | |
| 477 return data; | |
| 478 } | |
| 479 | |
| 480 void Font::cachePrimaryFont() const | |
| 481 { | |
| 482 ASSERT(m_fontList); | |
| 483 ASSERT(!m_cachedPrimaryFont); | |
| 484 m_cachedPrimaryFont = m_fontList->primaryFont(this)->fontDataForCharacter('
'); | |
| 485 } | |
| 486 | |
| 487 const FontData* Font::fontDataAt(unsigned index) const | |
| 488 { | |
| 489 ASSERT(m_fontList); | |
| 490 return m_fontList->fontDataAt(this, index); | |
| 491 } | |
| 492 | |
| 493 const FontData* Font::fontDataForCharacters(const UChar* characters, int length)
const | |
| 494 { | |
| 495 ASSERT(m_fontList); | |
| 496 return m_fontList->fontDataForCharacters(this, characters, length); | |
| 497 } | |
| 498 | |
| 499 void Font::update(PassRefPtr<FontSelector> fontSelector) const | |
| 500 { | |
| 501 // FIXME: It is pretty crazy that we are willing to just poke into a RefPtr,
but it ends up | |
| 502 // being reasonably safe (because inherited fonts in the render tree pick up
the new | |
| 503 // style anyway. Other copies are transient, e.g., the state in the Graphics
Context, and | |
| 504 // won't stick around long enough to get you in trouble). Still, this is pre
tty disgusting, | |
| 505 // and could eventually be rectified by using RefPtrs for Fonts themselves. | |
| 506 if (!m_fontList) | |
| 507 m_fontList = FontFallbackList::create(); | |
| 508 m_fontList->invalidate(fontSelector); | |
| 509 m_cachedPrimaryFont = 0; | |
| 510 m_pageZero = 0; | |
| 511 m_pages.clear(); | |
| 512 } | |
| 513 | |
| 514 int Font::width(const TextRun& run) const | |
| 515 { | |
| 516 return lroundf(floatWidth(run)); | |
| 517 } | |
| 518 | |
| 519 int Font::ascent() const | |
| 520 { | |
| 521 return primaryFont()->ascent(); | |
| 522 } | |
| 523 | |
| 524 int Font::descent() const | |
| 525 { | |
| 526 return primaryFont()->descent(); | |
| 527 } | |
| 528 | |
| 529 int Font::lineSpacing() const | |
| 530 { | |
| 531 return primaryFont()->lineSpacing(); | |
| 532 } | |
| 533 | |
| 534 int Font::lineGap() const | |
| 535 { | |
| 536 return primaryFont()->lineGap(); | |
| 537 } | |
| 538 | |
| 539 float Font::xHeight() const | |
| 540 { | |
| 541 return primaryFont()->xHeight(); | |
| 542 } | |
| 543 | |
| 544 unsigned Font::unitsPerEm() const | |
| 545 { | |
| 546 return primaryFont()->unitsPerEm(); | |
| 547 } | |
| 548 | |
| 549 int Font::spaceWidth() const | |
| 550 { | |
| 551 return (int)ceilf(primaryFont()->m_adjustedSpaceWidth + m_letterSpacing); | |
| 552 } | |
| 553 | |
| 554 bool Font::isFixedPitch() const | |
| 555 { | |
| 556 ASSERT(m_fontList); | |
| 557 return m_fontList->isFixedPitch(this); | |
| 558 } | |
| 559 | |
| 560 void Font::setCodePath(CodePath p) | |
| 561 { | |
| 562 codePath = p; | |
| 563 } | |
| 564 | |
| 565 bool Font::canUseGlyphCache(const TextRun& run) const | |
| 566 { | |
| 567 switch (codePath) { | |
| 568 case Auto: | |
| 569 break; | |
| 570 case Simple: | |
| 571 return true; | |
| 572 case Complex: | |
| 573 return false; | |
| 574 } | |
| 575 | |
| 576 // Start from 0 since drawing and highlighting also measure the characters b
efore run->from | |
| 577 for (int i = 0; i < run.length(); i++) { | |
| 578 const UChar c = run[i]; | |
| 579 if (c < 0x300) // U+0300 through U+036F Combining diacritical marks | |
| 580 continue; | |
| 581 if (c <= 0x36F) | |
| 582 return false; | |
| 583 | |
| 584 if (c < 0x0591 || c == 0x05BE) // U+0591 through U+05CF excluding U+
05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha | |
| 585 continue; | |
| 586 if (c <= 0x05CF) | |
| 587 return false; | |
| 588 | |
| 589 if (c < 0x0600) // U+0600 through U+1059 Arabic, Syriac, Thaana, Dev
anagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam,
Sinhala, Thai, Lao, Tibetan, Myanmar | |
| 590 continue; | |
| 591 if (c <= 0x1059) | |
| 592 return false; | |
| 593 | |
| 594 if (c < 0x1100) // U+1100 through U+11FF Hangul Jamo (only Ancient K
orean should be left here if you precompose; Modern Korean will be precomposed a
s a result of step A) | |
| 595 continue; | |
| 596 if (c <= 0x11FF) | |
| 597 return false; | |
| 598 | |
| 599 if (c < 0x1780) // U+1780 through U+18AF Khmer, Mongolian | |
| 600 continue; | |
| 601 if (c <= 0x18AF) | |
| 602 return false; | |
| 603 | |
| 604 if (c < 0x1900) // U+1900 through U+194F Limbu (Unicode 4.0) | |
| 605 continue; | |
| 606 if (c <= 0x194F) | |
| 607 return false; | |
| 608 | |
| 609 if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols | |
| 610 continue; | |
| 611 if (c <= 0x20FF) | |
| 612 return false; | |
| 613 | |
| 614 if (c < 0xFE20) // U+FE20 through U+FE2F Combining half marks | |
| 615 continue; | |
| 616 if (c <= 0xFE2F) | |
| 617 return false; | |
| 618 } | |
| 619 | |
| 620 return true; | |
| 621 | |
| 622 } | |
| 623 | |
| 624 void Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const Fl
oatPoint& point, int from, int to) const | |
| 625 { | |
| 626 // This glyph buffer holds our glyphs+advances+font data for each glyph. | |
| 627 GlyphBuffer glyphBuffer; | |
| 628 | |
| 629 float startX = point.x(); | |
| 630 WidthIterator it(this, run); | |
| 631 it.advance(from); | |
| 632 float beforeWidth = it.m_runWidthSoFar; | |
| 633 it.advance(to, &glyphBuffer); | |
| 634 | |
| 635 // We couldn't generate any glyphs for the run. Give up. | |
| 636 if (glyphBuffer.isEmpty()) | |
| 637 return; | |
| 638 | |
| 639 float afterWidth = it.m_runWidthSoFar; | |
| 640 | |
| 641 if (run.rtl()) { | |
| 642 float finalRoundingWidth = it.m_finalRoundingWidth; | |
| 643 it.advance(run.length()); | |
| 644 startX += finalRoundingWidth + it.m_runWidthSoFar - afterWidth; | |
| 645 } else | |
| 646 startX += beforeWidth; | |
| 647 | |
| 648 // Swap the order of the glyphs if right-to-left. | |
| 649 if (run.rtl()) | |
| 650 for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2
; ++i, --end) | |
| 651 glyphBuffer.swap(i, end); | |
| 652 | |
| 653 // Calculate the starting point of the glyphs to be displayed by adding | |
| 654 // all the advances up to the first glyph. | |
| 655 FloatPoint startPoint(startX, point.y()); | |
| 656 drawGlyphBuffer(context, glyphBuffer, run, startPoint); | |
| 657 } | |
| 658 | |
| 659 void Font::drawGlyphBuffer(GraphicsContext* context, const GlyphBuffer& glyphBuf
fer, | |
| 660 const TextRun& run, const FloatPoint& point) const | |
| 661 { | |
| 662 // Draw each contiguous run of glyphs that use the same font data. | |
| 663 const SimpleFontData* fontData = glyphBuffer.fontDataAt(0); | |
| 664 FloatSize offset = glyphBuffer.offsetAt(0); | |
| 665 FloatPoint startPoint(point); | |
| 666 float nextX = startPoint.x(); | |
| 667 int lastFrom = 0; | |
| 668 int nextGlyph = 0; | |
| 669 while (nextGlyph < glyphBuffer.size()) { | |
| 670 const SimpleFontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph); | |
| 671 FloatSize nextOffset = glyphBuffer.offsetAt(nextGlyph); | |
| 672 if (nextFontData != fontData || nextOffset != offset) { | |
| 673 drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - las
tFrom, startPoint); | |
| 674 | |
| 675 lastFrom = nextGlyph; | |
| 676 fontData = nextFontData; | |
| 677 offset = nextOffset; | |
| 678 startPoint.setX(nextX); | |
| 679 } | |
| 680 nextX += glyphBuffer.advanceAt(nextGlyph); | |
| 681 nextGlyph++; | |
| 682 } | |
| 683 | |
| 684 drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, s
tartPoint); | |
| 685 } | |
| 686 | |
| 687 void Font::drawText(GraphicsContext* context, const TextRun& run, const FloatPoi
nt& point, int from, int to) const | |
| 688 { | |
| 689 // Don't draw anything while we are using custom fonts that are in the proce
ss of loading. | |
| 690 if (m_fontList && m_fontList->loadingCustomFonts()) | |
| 691 return; | |
| 692 | |
| 693 to = (to == -1 ? run.length() : to); | |
| 694 | |
| 695 #if ENABLE(SVG_FONTS) | |
| 696 if (primaryFont()->isSVGFont()) { | |
| 697 drawTextUsingSVGFont(context, run, point, from, to); | |
| 698 return; | |
| 699 } | |
| 700 #endif | |
| 701 | |
| 702 if (canUseGlyphCache(run)) | |
| 703 drawSimpleText(context, run, point, from, to); | |
| 704 else | |
| 705 drawComplexText(context, run, point, from, to); | |
| 706 } | |
| 707 | |
| 708 float Font::floatWidth(const TextRun& run) const | |
| 709 { | |
| 710 #if ENABLE(SVG_FONTS) | |
| 711 if (primaryFont()->isSVGFont()) | |
| 712 return floatWidthUsingSVGFont(run); | |
| 713 #endif | |
| 714 | |
| 715 if (canUseGlyphCache(run)) | |
| 716 return floatWidthForSimpleText(run, 0); | |
| 717 return floatWidthForComplexText(run); | |
| 718 } | |
| 719 | |
| 720 float Font::floatWidth(const TextRun& run, int extraCharsAvailable, int& charsCo
nsumed, String& glyphName) const | |
| 721 { | |
| 722 #if ENABLE(SVG_FONTS) | |
| 723 if (primaryFont()->isSVGFont()) | |
| 724 return floatWidthUsingSVGFont(run, extraCharsAvailable, charsConsumed, g
lyphName); | |
| 725 #endif | |
| 726 | |
| 727 charsConsumed = run.length(); | |
| 728 glyphName = ""; | |
| 729 if (canUseGlyphCache(run)) | |
| 730 return floatWidthForSimpleText(run, 0); | |
| 731 return floatWidthForComplexText(run); | |
| 732 } | |
| 733 | |
| 734 float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer
) const | |
| 735 { | |
| 736 WidthIterator it(this, run); | |
| 737 it.advance(run.length(), glyphBuffer); | |
| 738 return it.m_runWidthSoFar; | |
| 739 } | |
| 740 | |
| 741 FloatRect Font::selectionRectForText(const TextRun& run, const IntPoint& point,
int h, int from, int to) const | |
| 742 { | |
| 743 #if ENABLE(SVG_FONTS) | |
| 744 if (primaryFont()->isSVGFont()) | |
| 745 return selectionRectForTextUsingSVGFont(run, point, h, from, to); | |
| 746 #endif | |
| 747 | |
| 748 to = (to == -1 ? run.length() : to); | |
| 749 if (canUseGlyphCache(run)) | |
| 750 return selectionRectForSimpleText(run, point, h, from, to); | |
| 751 return selectionRectForComplexText(run, point, h, from, to); | |
| 752 } | |
| 753 | |
| 754 FloatRect Font::selectionRectForSimpleText(const TextRun& run, const IntPoint& p
oint, int h, int from, int to) const | |
| 755 { | |
| 756 WidthIterator it(this, run); | |
| 757 it.advance(from); | |
| 758 float beforeWidth = it.m_runWidthSoFar; | |
| 759 it.advance(to); | |
| 760 float afterWidth = it.m_runWidthSoFar; | |
| 761 | |
| 762 // Using roundf() rather than ceilf() for the right edge as a compromise to
ensure correct caret positioning | |
| 763 if (run.rtl()) { | |
| 764 it.advance(run.length()); | |
| 765 float totalWidth = it.m_runWidthSoFar; | |
| 766 return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(),
roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h); | |
| 767 } else { | |
| 768 return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afte
rWidth) - floorf(beforeWidth), h); | |
| 769 } | |
| 770 } | |
| 771 | |
| 772 int Font::offsetForPosition(const TextRun& run, int x, bool includePartialGlyphs
) const | |
| 773 { | |
| 774 #if ENABLE(SVG_FONTS) | |
| 775 if (primaryFont()->isSVGFont()) | |
| 776 return offsetForPositionForTextUsingSVGFont(run, x, includePartialGlyphs
); | |
| 777 #endif | |
| 778 | |
| 779 if (canUseGlyphCache(run)) | |
| 780 return offsetForPositionForSimpleText(run, x, includePartialGlyphs); | |
| 781 return offsetForPositionForComplexText(run, x, includePartialGlyphs); | |
| 782 } | |
| 783 | |
| 784 int Font::offsetForPositionForSimpleText(const TextRun& run, int x, bool include
PartialGlyphs) const | |
| 785 { | |
| 786 float delta = (float)x; | |
| 787 | |
| 788 WidthIterator it(this, run); | |
| 789 GlyphBuffer localGlyphBuffer; | |
| 790 unsigned offset; | |
| 791 if (run.rtl()) { | |
| 792 delta -= floatWidthForSimpleText(run, 0); | |
| 793 while (1) { | |
| 794 offset = it.m_currentCharacter; | |
| 795 float w; | |
| 796 if (!it.advanceOneCharacter(w, &localGlyphBuffer)) | |
| 797 break; | |
| 798 delta += w; | |
| 799 if (includePartialGlyphs) { | |
| 800 if (delta - w / 2 >= 0) | |
| 801 break; | |
| 802 } else { | |
| 803 if (delta >= 0) | |
| 804 break; | |
| 805 } | |
| 806 } | |
| 807 } else { | |
| 808 while (1) { | |
| 809 offset = it.m_currentCharacter; | |
| 810 float w; | |
| 811 if (!it.advanceOneCharacter(w, &localGlyphBuffer)) | |
| 812 break; | |
| 813 delta -= w; | |
| 814 if (includePartialGlyphs) { | |
| 815 if (delta + w / 2 <= 0) | |
| 816 break; | |
| 817 } else { | |
| 818 if (delta <= 0) | |
| 819 break; | |
| 820 } | |
| 821 } | |
| 822 } | |
| 823 | |
| 824 return offset; | |
| 825 } | |
| 826 | |
| 827 #if ENABLE(SVG_FONTS) | |
| 828 bool Font::isSVGFont() const | |
| 829 { | |
| 830 return primaryFont()->isSVGFont(); | |
| 831 } | |
| 832 #endif | |
| 833 | |
| 834 FontSelector* Font::fontSelector() const | |
| 835 { | |
| 836 return m_fontList ? m_fontList->fontSelector() : 0; | |
| 837 } | |
| 838 | |
| 839 } | |
| OLD | NEW |