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 |