OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2012 Google Inc. All rights reserved. | |
3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions are | |
7 * met: | |
8 * | |
9 * * Redistributions of source code must retain the above copyright | |
10 * notice, this list of conditions and the following disclaimer. | |
11 * * Redistributions in binary form must reproduce the above | |
12 * copyright notice, this list of conditions and the following disclaimer | |
13 * in the documentation and/or other materials provided with the | |
14 * distribution. | |
15 * * Neither the name of Google Inc. nor the names of its | |
16 * contributors may be used to endorse or promote products derived from | |
17 * this software without specific prior written permission. | |
18 * | |
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
30 */ | |
31 | |
32 #include "config.h" | |
33 #include "core/platform/graphics/harfbuzz/HarfBuzzShaper.h" | |
34 | |
35 #include "RuntimeEnabledFeatures.h" | |
36 #include "core/platform/graphics/Font.h" | |
37 #include "core/platform/graphics/harfbuzz/HarfBuzzFace.h" | |
38 #include "hb-icu.h" | |
39 #include "platform/text/SurrogatePairAwareTextIterator.h" | |
40 #include "wtf/MathExtras.h" | |
41 #include "wtf/unicode/Unicode.h" | |
42 #include <unicode/normlzr.h> | |
43 #include <unicode/uchar.h> | |
44 | |
45 #include <list> | |
46 #include <map> | |
47 #include <string> | |
48 | |
49 namespace WebCore { | |
50 | |
51 template<typename T> | |
52 class HarfBuzzScopedPtr { | |
53 public: | |
54 typedef void (*DestroyFunction)(T*); | |
55 | |
56 HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy) | |
57 : m_ptr(ptr) | |
58 , m_destroy(destroy) | |
59 { | |
60 ASSERT(m_destroy); | |
61 } | |
62 ~HarfBuzzScopedPtr() | |
63 { | |
64 if (m_ptr) | |
65 (*m_destroy)(m_ptr); | |
66 } | |
67 | |
68 T* get() { return m_ptr; } | |
69 void set(T* ptr) { m_ptr = ptr; } | |
70 private: | |
71 T* m_ptr; | |
72 DestroyFunction m_destroy; | |
73 }; | |
74 | |
75 | |
76 static const unsigned cHarfBuzzCacheMaxSize = 256; | |
77 | |
78 struct CachedShapingResultsLRUNode; | |
79 struct CachedShapingResults; | |
80 typedef std::map<std::wstring, CachedShapingResults*> CachedShapingResultsMap; | |
81 typedef std::list<CachedShapingResultsLRUNode*> CachedShapingResultsLRU; | |
82 | |
83 struct CachedShapingResults { | |
84 CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* runFont, hb_di
rection_t runDir); | |
85 ~CachedShapingResults(); | |
86 | |
87 hb_buffer_t* buffer; | |
88 Font font; | |
89 hb_direction_t dir; | |
90 CachedShapingResultsLRU::iterator lru; | |
91 }; | |
92 | |
93 struct CachedShapingResultsLRUNode { | |
94 CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEn
try); | |
95 ~CachedShapingResultsLRUNode(); | |
96 | |
97 CachedShapingResultsMap::iterator entry; | |
98 }; | |
99 | |
100 CachedShapingResults::CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Fo
nt* fontData, hb_direction_t dirData) | |
101 : buffer(harfBuzzBuffer) | |
102 , font(*fontData) | |
103 , dir(dirData) | |
104 { | |
105 } | |
106 | |
107 CachedShapingResults::~CachedShapingResults() | |
108 { | |
109 hb_buffer_destroy(buffer); | |
110 } | |
111 | |
112 CachedShapingResultsLRUNode::CachedShapingResultsLRUNode(const CachedShapingResu
ltsMap::iterator& cacheEntry) | |
113 : entry(cacheEntry) | |
114 { | |
115 } | |
116 | |
117 CachedShapingResultsLRUNode::~CachedShapingResultsLRUNode() | |
118 { | |
119 } | |
120 | |
121 class HarfBuzzRunCache { | |
122 public: | |
123 HarfBuzzRunCache(); | |
124 ~HarfBuzzRunCache(); | |
125 | |
126 CachedShapingResults* find(const std::wstring& key) const; | |
127 void remove(CachedShapingResults* node); | |
128 void moveToBack(CachedShapingResults* node); | |
129 bool insert(const std::wstring& key, CachedShapingResults* run); | |
130 | |
131 private: | |
132 CachedShapingResultsMap m_harfBuzzRunMap; | |
133 CachedShapingResultsLRU m_harfBuzzRunLRU; | |
134 }; | |
135 | |
136 | |
137 HarfBuzzRunCache::HarfBuzzRunCache() | |
138 { | |
139 } | |
140 | |
141 HarfBuzzRunCache::~HarfBuzzRunCache() | |
142 { | |
143 for (CachedShapingResultsMap::iterator it = m_harfBuzzRunMap.begin(); it !=
m_harfBuzzRunMap.end(); ++it) | |
144 delete it->second; | |
145 for (CachedShapingResultsLRU::iterator it = m_harfBuzzRunLRU.begin(); it !=
m_harfBuzzRunLRU.end(); ++it) | |
146 delete *it; | |
147 } | |
148 | |
149 bool HarfBuzzRunCache::insert(const std::wstring& key, CachedShapingResults* dat
a) | |
150 { | |
151 std::pair<CachedShapingResultsMap::iterator, bool> results = | |
152 m_harfBuzzRunMap.insert(CachedShapingResultsMap::value_type(key, data)); | |
153 | |
154 if (!results.second) | |
155 return false; | |
156 | |
157 CachedShapingResultsLRUNode* node = new CachedShapingResultsLRUNode(results.
first); | |
158 | |
159 m_harfBuzzRunLRU.push_back(node); | |
160 data->lru = --m_harfBuzzRunLRU.end(); | |
161 | |
162 if (m_harfBuzzRunMap.size() > cHarfBuzzCacheMaxSize) { | |
163 CachedShapingResultsLRUNode* lru = m_harfBuzzRunLRU.front(); | |
164 CachedShapingResults* foo = lru->entry->second; | |
165 m_harfBuzzRunMap.erase(lru->entry); | |
166 m_harfBuzzRunLRU.pop_front(); | |
167 delete foo; | |
168 delete lru; | |
169 } | |
170 | |
171 return true; | |
172 } | |
173 | |
174 inline CachedShapingResults* HarfBuzzRunCache::find(const std::wstring& key) con
st | |
175 { | |
176 CachedShapingResultsMap::const_iterator it = m_harfBuzzRunMap.find(key); | |
177 | |
178 return it != m_harfBuzzRunMap.end() ? it->second : 0; | |
179 } | |
180 | |
181 inline void HarfBuzzRunCache::remove(CachedShapingResults* node) | |
182 { | |
183 CachedShapingResultsLRUNode* lruNode = *node->lru; | |
184 | |
185 m_harfBuzzRunLRU.erase(node->lru); | |
186 m_harfBuzzRunMap.erase(lruNode->entry); | |
187 delete lruNode; | |
188 delete node; | |
189 } | |
190 | |
191 inline void HarfBuzzRunCache::moveToBack(CachedShapingResults* node) | |
192 { | |
193 CachedShapingResultsLRUNode* lruNode = *node->lru; | |
194 m_harfBuzzRunLRU.erase(node->lru); | |
195 m_harfBuzzRunLRU.push_back(lruNode); | |
196 node->lru = --m_harfBuzzRunLRU.end(); | |
197 } | |
198 | |
199 HarfBuzzRunCache& harfBuzzRunCache() | |
200 { | |
201 DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ()); | |
202 return globalHarfBuzzRunCache; | |
203 } | |
204 | |
205 static inline float harfBuzzPositionToFloat(hb_position_t value) | |
206 { | |
207 return static_cast<float>(value) / (1 << 16); | |
208 } | |
209 | |
210 inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData,
unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_
t script) | |
211 : m_fontData(fontData) | |
212 , m_startIndex(startIndex) | |
213 , m_numCharacters(numCharacters) | |
214 , m_numGlyphs(0) | |
215 , m_direction(direction) | |
216 , m_script(script) | |
217 , m_width(0) | |
218 { | |
219 } | |
220 | |
221 inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const HarfBuzzRun& rhs) | |
222 : m_fontData(rhs.m_fontData) | |
223 , m_startIndex(rhs.m_startIndex) | |
224 , m_numCharacters(rhs.m_numCharacters) | |
225 , m_numGlyphs(rhs.m_numGlyphs) | |
226 , m_direction(rhs.m_direction) | |
227 , m_script(rhs.m_script) | |
228 , m_glyphs(rhs.m_glyphs) | |
229 , m_advances(rhs.m_advances) | |
230 , m_glyphToCharacterIndexes(rhs.m_glyphToCharacterIndexes) | |
231 , m_offsets(rhs.m_offsets) | |
232 , m_width(rhs.m_width) | |
233 { | |
234 } | |
235 | |
236 HarfBuzzShaper::HarfBuzzRun::~HarfBuzzRun() | |
237 { | |
238 } | |
239 | |
240 inline void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfBuzzB
uffer) | |
241 { | |
242 m_numGlyphs = hb_buffer_get_length(harfBuzzBuffer); | |
243 m_glyphs.resize(m_numGlyphs); | |
244 m_advances.resize(m_numGlyphs); | |
245 m_glyphToCharacterIndexes.resize(m_numGlyphs); | |
246 m_offsets.resize(m_numGlyphs); | |
247 } | |
248 | |
249 inline void HarfBuzzShaper::HarfBuzzRun::copyShapeResultAndGlyphPositions(const
HarfBuzzRun& run) | |
250 { | |
251 m_numGlyphs = run.m_numGlyphs; | |
252 m_glyphs = run.m_glyphs; | |
253 m_advances = run.m_advances; | |
254 m_glyphToCharacterIndexes = run.m_glyphToCharacterIndexes; | |
255 m_offsets = run.m_offsets; | |
256 m_width = run.m_width; | |
257 } | |
258 | |
259 inline void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, ui
nt16_t glyphId, float advance, float offsetX, float offsetY) | |
260 { | |
261 m_glyphs[index] = glyphId; | |
262 m_advances[index] = advance; | |
263 m_offsets[index] = FloatPoint(offsetX, offsetY); | |
264 } | |
265 | |
266 int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX) | |
267 { | |
268 ASSERT(targetX <= m_width); | |
269 float currentX = 0; | |
270 float currentAdvance = m_advances[0]; | |
271 unsigned glyphIndex = 0; | |
272 | |
273 // Sum up advances that belong to a character. | |
274 while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex]
== m_glyphToCharacterIndexes[glyphIndex + 1]) | |
275 currentAdvance += m_advances[++glyphIndex]; | |
276 currentAdvance = currentAdvance / 2.0; | |
277 if (targetX <= currentAdvance) | |
278 return rtl() ? m_numCharacters : 0; | |
279 | |
280 ++glyphIndex; | |
281 while (glyphIndex < m_numGlyphs) { | |
282 unsigned prevCharacterIndex = m_glyphToCharacterIndexes[glyphIndex - 1]; | |
283 float prevAdvance = currentAdvance; | |
284 currentAdvance = m_advances[glyphIndex]; | |
285 while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIn
dex] == m_glyphToCharacterIndexes[glyphIndex + 1]) | |
286 currentAdvance += m_advances[++glyphIndex]; | |
287 currentAdvance = currentAdvance / 2.0; | |
288 float nextX = currentX + prevAdvance + currentAdvance; | |
289 if (currentX <= targetX && targetX <= nextX) | |
290 return rtl() ? prevCharacterIndex : m_glyphToCharacterIndexes[glyphI
ndex]; | |
291 currentX = nextX; | |
292 prevAdvance = currentAdvance; | |
293 ++glyphIndex; | |
294 } | |
295 | |
296 return rtl() ? 0 : m_numCharacters; | |
297 } | |
298 | |
299 float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset) | |
300 { | |
301 ASSERT(offset < m_numCharacters); | |
302 unsigned glyphIndex = 0; | |
303 float position = 0; | |
304 if (rtl()) { | |
305 while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex]
> offset) { | |
306 position += m_advances[glyphIndex]; | |
307 ++glyphIndex; | |
308 } | |
309 // For RTL, we need to return the right side boundary of the character. | |
310 // Add advance of glyphs which are part of the character. | |
311 while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIn
dex] == m_glyphToCharacterIndexes[glyphIndex + 1]) { | |
312 position += m_advances[glyphIndex]; | |
313 ++glyphIndex; | |
314 } | |
315 position += m_advances[glyphIndex]; | |
316 } else { | |
317 while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex]
< offset) { | |
318 position += m_advances[glyphIndex]; | |
319 ++glyphIndex; | |
320 } | |
321 } | |
322 return position; | |
323 } | |
324 | |
325 static void normalizeCharacters(const TextRun& run, unsigned length, UChar* dest
ination, unsigned* destinationLength) | |
326 { | |
327 unsigned position = 0; | |
328 bool error = false; | |
329 const UChar* source; | |
330 String stringFor8BitRun; | |
331 if (run.is8Bit()) { | |
332 stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), ru
n.length()); | |
333 source = stringFor8BitRun.characters16(); | |
334 } else | |
335 source = run.characters16(); | |
336 | |
337 *destinationLength = 0; | |
338 while (position < length) { | |
339 UChar32 character; | |
340 U16_NEXT(source, position, length, character); | |
341 // Don't normalize tabs as they are not treated as spaces for word-end. | |
342 if (Font::treatAsSpace(character) && character != '\t') | |
343 character = ' '; | |
344 else if (Font::treatAsZeroWidthSpaceInComplexScript(character)) | |
345 character = zeroWidthSpace; | |
346 U16_APPEND(destination, *destinationLength, length, character, error); | |
347 ASSERT_UNUSED(error, !error); | |
348 } | |
349 } | |
350 | |
351 HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run) | |
352 : m_font(font) | |
353 , m_normalizedBufferLength(0) | |
354 , m_run(run) | |
355 , m_wordSpacingAdjustment(font->wordSpacing()) | |
356 , m_padding(0) | |
357 , m_padPerWordBreak(0) | |
358 , m_padError(0) | |
359 , m_letterSpacing(font->letterSpacing()) | |
360 , m_fromIndex(0) | |
361 , m_toIndex(m_run.length()) | |
362 { | |
363 m_normalizedBuffer = adoptArrayPtr(new UChar[m_run.length() + 1]); | |
364 normalizeCharacters(m_run, m_run.length(), m_normalizedBuffer.get(), &m_norm
alizedBufferLength); | |
365 setPadding(m_run.expansion()); | |
366 setFontFeatures(); | |
367 } | |
368 | |
369 static void normalizeSpacesAndMirrorChars(const UChar* source, unsigned length,
UChar* destination, unsigned* destinationLength, HarfBuzzShaper::NormalizeMode n
ormalizeMode) | |
370 { | |
371 unsigned position = 0; | |
372 bool error = false; | |
373 // Iterate characters in source and mirror character if needed. | |
374 *destinationLength = 0; | |
375 while (position < length) { | |
376 UChar32 character; | |
377 U16_NEXT(source, position, length, character); | |
378 // Don't normalize tabs as they are not treated as spaces for word-end | |
379 if (Font::treatAsSpace(character) && character != '\t') | |
380 character = ' '; | |
381 else if (Font::treatAsZeroWidthSpace(character)) | |
382 character = zeroWidthSpace; | |
383 else if (normalizeMode == HarfBuzzShaper::NormalizeMirrorChars) | |
384 character = u_charMirror(character); | |
385 U16_APPEND(destination, *destinationLength, length, character, error); | |
386 ASSERT_UNUSED(error, !error); | |
387 } | |
388 } | |
389 | |
390 void HarfBuzzShaper::setNormalizedBuffer(NormalizeMode normalizeMode) | |
391 { | |
392 // Normalize the text run in three ways: | |
393 // 1) Convert the |originalRun| to NFC normalized form if combining diacriti
cal marks | |
394 // (U+0300..) are used in the run. This conversion is necessary since most O
penType | |
395 // fonts (e.g., Arial) don't have substitution rules for the diacritical mar
ks in | |
396 // their GSUB tables. | |
397 // | |
398 // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API h
ere since | |
399 // the API returns FALSE (= not normalized) for complex runs that don't requ
ire NFC | |
400 // normalization (e.g., Arabic text). Unless the run contains the diacritica
l marks, | |
401 // HarfBuzz will do the same thing for us using the GSUB table. | |
402 // 2) Convert spacing characters into plain spaces, as some fonts will provi
de glyphs | |
403 // for characters like '\n' otherwise. | |
404 // 3) Convert mirrored characters such as parenthesis for rtl text. | |
405 | |
406 // Convert to NFC form if the text has diacritical marks. | |
407 icu::UnicodeString normalizedString; | |
408 UErrorCode error = U_ZERO_ERROR; | |
409 | |
410 const UChar* runCharacters; | |
411 String stringFor8BitRun; | |
412 if (m_run.is8Bit()) { | |
413 stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(),
m_run.length()); | |
414 runCharacters = stringFor8BitRun.characters16(); | |
415 } else | |
416 runCharacters = m_run.characters16(); | |
417 | |
418 for (int i = 0; i < m_run.length(); ++i) { | |
419 UChar ch = runCharacters[i]; | |
420 if (::ublock_getCode(ch) == UBLOCK_COMBINING_DIACRITICAL_MARKS) { | |
421 icu::Normalizer::normalize(icu::UnicodeString(runCharacters, | |
422 m_run.length()), UNORM_NFC, 0 /* no options */, | |
423 normalizedString, error); | |
424 if (U_FAILURE(error)) | |
425 normalizedString.remove(); | |
426 break; | |
427 } | |
428 } | |
429 | |
430 const UChar* sourceText; | |
431 unsigned sourceLength; | |
432 if (normalizedString.isEmpty()) { | |
433 sourceLength = m_run.length(); | |
434 sourceText = runCharacters; | |
435 } else { | |
436 sourceLength = normalizedString.length(); | |
437 sourceText = normalizedString.getBuffer(); | |
438 } | |
439 | |
440 m_normalizedBuffer = adoptArrayPtr(new UChar[sourceLength + 1]); | |
441 normalizeSpacesAndMirrorChars(sourceText, sourceLength, m_normalizedBuffer.g
et(), &m_normalizedBufferLength, normalizeMode); | |
442 } | |
443 | |
444 bool HarfBuzzShaper::isWordEnd(unsigned index) | |
445 { | |
446 // This could refer a high-surrogate, but should work. | |
447 return index && isCodepointSpace(m_normalizedBuffer[index]); | |
448 } | |
449 | |
450 int HarfBuzzShaper::determineWordBreakSpacing() | |
451 { | |
452 int wordBreakSpacing = m_wordSpacingAdjustment; | |
453 | |
454 if (m_padding > 0) { | |
455 int toPad = roundf(m_padPerWordBreak + m_padError); | |
456 m_padError += m_padPerWordBreak - toPad; | |
457 | |
458 if (m_padding < toPad) | |
459 toPad = m_padding; | |
460 m_padding -= toPad; | |
461 wordBreakSpacing += toPad; | |
462 } | |
463 return wordBreakSpacing; | |
464 } | |
465 | |
466 // setPadding sets a number of pixels to be distributed across the TextRun. | |
467 // WebKit uses this to justify text. | |
468 void HarfBuzzShaper::setPadding(int padding) | |
469 { | |
470 m_padding = padding; | |
471 m_padError = 0; | |
472 if (!m_padding) | |
473 return; | |
474 | |
475 // If we have padding to distribute, then we try to give an equal | |
476 // amount to each space. The last space gets the smaller amount, if | |
477 // any. | |
478 unsigned numWordEnds = 0; | |
479 | |
480 for (unsigned i = 0; i < m_normalizedBufferLength; i++) { | |
481 if (isWordEnd(i)) | |
482 numWordEnds++; | |
483 } | |
484 | |
485 if (numWordEnds) | |
486 m_padPerWordBreak = m_padding / numWordEnds; | |
487 else | |
488 m_padPerWordBreak = 0; | |
489 } | |
490 | |
491 | |
492 void HarfBuzzShaper::setDrawRange(int from, int to) | |
493 { | |
494 ASSERT_WITH_SECURITY_IMPLICATION(from >= 0); | |
495 ASSERT_WITH_SECURITY_IMPLICATION(to <= m_run.length()); | |
496 m_fromIndex = from; | |
497 m_toIndex = to; | |
498 } | |
499 | |
500 void HarfBuzzShaper::setFontFeatures() | |
501 { | |
502 const FontDescription& description = m_font->fontDescription(); | |
503 if (description.orientation() == Vertical) { | |
504 static hb_feature_t vert = { HarfBuzzFace::vertTag, 1, 0, static_cast<un
signed>(-1) }; | |
505 static hb_feature_t vrt2 = { HarfBuzzFace::vrt2Tag, 1, 0, static_cast<un
signed>(-1) }; | |
506 m_features.append(vert); | |
507 m_features.append(vrt2); | |
508 } | |
509 | |
510 static hb_feature_t noKern = { HB_TAG('k', 'e', 'r', 'n'), 0, 0, static_cast
<unsigned>(-1) }; | |
511 static hb_feature_t noVkrn = { HB_TAG('v', 'k', 'r', 'n'), 0, 0, static_cast
<unsigned>(-1) }; | |
512 switch (description.kerning()) { | |
513 case FontDescription::NormalKerning: | |
514 // kern/vkrn are enabled by default | |
515 break; | |
516 case FontDescription::NoneKerning: | |
517 m_features.append(description.orientation() == Vertical ? noVkrn : noKer
n); | |
518 break; | |
519 case FontDescription::AutoKerning: | |
520 break; | |
521 } | |
522 | |
523 FontFeatureSettings* settings = description.featureSettings(); | |
524 if (!settings) | |
525 return; | |
526 | |
527 unsigned numFeatures = settings->size(); | |
528 for (unsigned i = 0; i < numFeatures; ++i) { | |
529 hb_feature_t feature; | |
530 const AtomicString& tag = settings->at(i).tag(); | |
531 feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]); | |
532 feature.value = settings->at(i).value(); | |
533 feature.start = 0; | |
534 feature.end = static_cast<unsigned>(-1); | |
535 m_features.append(feature); | |
536 } | |
537 } | |
538 | |
539 bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer) | |
540 { | |
541 if (!collectHarfBuzzRuns()) | |
542 return false; | |
543 | |
544 m_totalWidth = 0; | |
545 // WebKit doesn't set direction when calulating widths. Leave the direction
setting to | |
546 // HarfBuzz when we are calculating widths (except when directionalOverride(
) is set). | |
547 if (!shapeHarfBuzzRuns(glyphBuffer || m_run.directionalOverride())) | |
548 return false; | |
549 | |
550 if (!RuntimeEnabledFeatures::subpixelFontScalingEnabled()) | |
551 m_totalWidth = roundf(m_totalWidth); | |
552 | |
553 if (glyphBuffer && !fillGlyphBuffer(glyphBuffer)) | |
554 return false; | |
555 | |
556 return true; | |
557 } | |
558 | |
559 FloatPoint HarfBuzzShaper::adjustStartPoint(const FloatPoint& point) | |
560 { | |
561 return point + m_startOffset; | |
562 } | |
563 | |
564 bool HarfBuzzShaper::collectHarfBuzzRuns() | |
565 { | |
566 const UChar* normalizedBufferEnd = m_normalizedBuffer.get() + m_normalizedBu
fferLength; | |
567 SurrogatePairAwareTextIterator iterator(m_normalizedBuffer.get(), 0, m_norma
lizedBufferLength, m_normalizedBufferLength); | |
568 UChar32 character; | |
569 unsigned clusterLength = 0; | |
570 unsigned startIndexOfCurrentRun = 0; | |
571 if (!iterator.consume(character, clusterLength)) | |
572 return false; | |
573 | |
574 const SimpleFontData* nextFontData = m_font->glyphDataForCharacter(character
, false).fontData; | |
575 UErrorCode errorCode = U_ZERO_ERROR; | |
576 UScriptCode nextScript = uscript_getScript(character, &errorCode); | |
577 if (U_FAILURE(errorCode)) | |
578 return false; | |
579 | |
580 do { | |
581 const UChar* currentCharacterPosition = iterator.characters(); | |
582 const SimpleFontData* currentFontData = nextFontData; | |
583 UScriptCode currentScript = nextScript; | |
584 | |
585 for (iterator.advance(clusterLength); iterator.consume(character, cluste
rLength); iterator.advance(clusterLength)) { | |
586 if (Font::treatAsZeroWidthSpace(character)) | |
587 continue; | |
588 | |
589 if (U_GET_GC_MASK(character) & U_GC_M_MASK) { | |
590 int markLength = clusterLength; | |
591 const UChar* markCharactersEnd = iterator.characters() + cluster
Length; | |
592 while (markCharactersEnd < normalizedBufferEnd) { | |
593 UChar32 nextCharacter; | |
594 int nextCharacterLength = 0; | |
595 U16_NEXT(markCharactersEnd, nextCharacterLength, normalizedB
ufferEnd - markCharactersEnd, nextCharacter); | |
596 if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK)) | |
597 break; | |
598 markLength += nextCharacterLength; | |
599 markCharactersEnd += nextCharacterLength; | |
600 } | |
601 | |
602 if (currentFontData->canRenderCombiningCharacterSequence(current
CharacterPosition, markCharactersEnd - currentCharacterPosition)) { | |
603 clusterLength = markLength; | |
604 continue; | |
605 } | |
606 } | |
607 | |
608 nextFontData = m_font->glyphDataForCharacter(character, false).fontD
ata; | |
609 nextScript = uscript_getScript(character, &errorCode); | |
610 if (U_FAILURE(errorCode)) | |
611 return false; | |
612 if ((nextFontData != currentFontData) || ((currentScript != nextScri
pt) && (nextScript != USCRIPT_INHERITED) && (!uscript_hasScript(character, curre
ntScript)))) | |
613 break; | |
614 if (nextScript == USCRIPT_INHERITED) | |
615 nextScript = currentScript; | |
616 currentCharacterPosition = iterator.characters(); | |
617 } | |
618 unsigned numCharactersOfCurrentRun = iterator.currentCharacter() - start
IndexOfCurrentRun; | |
619 hb_script_t script = hb_icu_script_to_script(currentScript); | |
620 m_harfBuzzRuns.append(HarfBuzzRun::create(currentFontData, startIndexOfC
urrentRun, numCharactersOfCurrentRun, m_run.direction(), script)); | |
621 currentFontData = nextFontData; | |
622 startIndexOfCurrentRun = iterator.currentCharacter(); | |
623 } while (iterator.consume(character, clusterLength)); | |
624 | |
625 return !m_harfBuzzRuns.isEmpty(); | |
626 } | |
627 | |
628 static const uint16_t* toUint16(const UChar* src) | |
629 { | |
630 // FIXME: This relies on undefined behavior however it works on the | |
631 // current versions of all compilers we care about and avoids making | |
632 // a copy of the string. | |
633 COMPILE_ASSERT(sizeof(UChar) == sizeof(uint16_t), UChar_is_the_same_size_as_
uint16_t); | |
634 return reinterpret_cast<const uint16_t*>(src); | |
635 } | |
636 | |
637 bool HarfBuzzShaper::shapeHarfBuzzRuns(bool shouldSetDirection) | |
638 { | |
639 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_
destroy); | |
640 | |
641 hb_buffer_set_unicode_funcs(harfBuzzBuffer.get(), hb_icu_get_unicode_funcs()
); | |
642 HarfBuzzRunCache& runCache = harfBuzzRunCache(); | |
643 | |
644 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { | |
645 unsigned runIndex = m_run.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; | |
646 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); | |
647 const SimpleFontData* currentFontData = currentRun->fontData(); | |
648 if (currentFontData->isSVGFont()) | |
649 return false; | |
650 | |
651 FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tF
ontData->platformData()); | |
652 HarfBuzzFace* face = platformData->harfBuzzFace(); | |
653 if (!face) | |
654 return false; | |
655 | |
656 hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script()); | |
657 if (shouldSetDirection) | |
658 hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->rtl() ? HB
_DIRECTION_RTL : HB_DIRECTION_LTR); | |
659 else | |
660 // Leaving direction to HarfBuzz to guess is *really* bad, but will
do for now. | |
661 hb_buffer_guess_segment_properties(harfBuzzBuffer.get()); | |
662 | |
663 hb_segment_properties_t props; | |
664 hb_buffer_get_segment_properties(harfBuzzBuffer.get(), &props); | |
665 | |
666 const UChar* src = m_normalizedBuffer.get() + currentRun->startIndex(); | |
667 std::wstring key(src, src + currentRun->numCharacters()); | |
668 | |
669 CachedShapingResults* cachedResults = runCache.find(key); | |
670 if (cachedResults) { | |
671 if (cachedResults->dir == props.direction && cachedResults->font ==
*m_font) { | |
672 currentRun->applyShapeResult(cachedResults->buffer); | |
673 setGlyphPositionsForHarfBuzzRun(currentRun, cachedResults->buffe
r); | |
674 | |
675 hb_buffer_reset(harfBuzzBuffer.get()); | |
676 | |
677 runCache.moveToBack(cachedResults); | |
678 | |
679 continue; | |
680 } | |
681 | |
682 runCache.remove(cachedResults); | |
683 } | |
684 | |
685 // Add a space as pre-context to the buffer. This prevents showing dotte
d-circle | |
686 // for combining marks at the beginning of runs. | |
687 static const uint16_t preContext = ' '; | |
688 hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0); | |
689 | |
690 if (m_font->isSmallCaps() && u_islower(m_normalizedBuffer[currentRun->st
artIndex()])) { | |
691 String upperText = String(m_normalizedBuffer.get() + currentRun->sta
rtIndex(), currentRun->numCharacters()).upper(); | |
692 currentFontData = m_font->glyphDataForCharacter(upperText[0], false,
SmallCapsVariant).fontData; | |
693 ASSERT(!upperText.is8Bit()); // m_normalizedBuffer is 16 bit, theref
ore upperText is 16 bit, even after we call makeUpper(). | |
694 hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(upperText.charact
ers16()), currentRun->numCharacters(), 0, currentRun->numCharacters()); | |
695 } else { | |
696 hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(m_normalizedBuffe
r.get() + currentRun->startIndex()), currentRun->numCharacters(), 0, currentRun-
>numCharacters()); | |
697 } | |
698 | |
699 if (m_font->fontDescription().orientation() == Vertical) | |
700 face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get()); | |
701 | |
702 HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_de
stroy); | |
703 | |
704 hb_shape(harfBuzzFont.get(), harfBuzzBuffer.get(), m_features.isEmpty()
? 0 : m_features.data(), m_features.size()); | |
705 currentRun->applyShapeResult(harfBuzzBuffer.get()); | |
706 setGlyphPositionsForHarfBuzzRun(currentRun, harfBuzzBuffer.get()); | |
707 | |
708 runCache.insert(key, new CachedShapingResults(harfBuzzBuffer.get(), m_fo
nt, props.direction)); | |
709 | |
710 harfBuzzBuffer.set(hb_buffer_create()); | |
711 hb_buffer_set_unicode_funcs(harfBuzzBuffer.get(), hb_icu_get_unicode_fun
cs()); | |
712 } | |
713 | |
714 return true; | |
715 } | |
716 | |
717 void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb
_buffer_t* harfBuzzBuffer) | |
718 { | |
719 const SimpleFontData* currentFontData = currentRun->fontData(); | |
720 hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); | |
721 hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzz
Buffer, 0); | |
722 | |
723 unsigned numGlyphs = currentRun->numGlyphs(); | |
724 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); | |
725 float totalAdvance = 0; | |
726 | |
727 // HarfBuzz returns the shaping result in visual order. We need not to flip
for RTL. | |
728 for (size_t i = 0; i < numGlyphs; ++i) { | |
729 bool runEnd = i + 1 == numGlyphs; | |
730 uint16_t glyph = glyphInfos[i].codepoint; | |
731 float offsetX = harfBuzzPositionToFloat(glyphPositions[i].x_offset); | |
732 float offsetY = -harfBuzzPositionToFloat(glyphPositions[i].y_offset); | |
733 float advance = harfBuzzPositionToFloat(glyphPositions[i].x_advance); | |
734 | |
735 unsigned currentCharacterIndex = currentRun->startIndex() + glyphInfos[i
].cluster; | |
736 bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1]
.cluster; | |
737 float spacing = 0; | |
738 | |
739 glyphToCharacterIndexes[i] = glyphInfos[i].cluster; | |
740 | |
741 if (isClusterEnd && !Font::treatAsZeroWidthSpace(m_normalizedBuffer[curr
entCharacterIndex])) | |
742 spacing += m_letterSpacing; | |
743 | |
744 if (isClusterEnd && isWordEnd(currentCharacterIndex)) | |
745 spacing += determineWordBreakSpacing(); | |
746 | |
747 if (currentFontData->isZeroWidthSpaceGlyph(glyph)) { | |
748 currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0); | |
749 continue; | |
750 } | |
751 | |
752 advance += spacing; | |
753 if (m_run.rtl()) { | |
754 // In RTL, spacing should be added to left side of glyphs. | |
755 offsetX += spacing; | |
756 if (!isClusterEnd) | |
757 offsetX += m_letterSpacing; | |
758 } | |
759 | |
760 currentRun->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY); | |
761 | |
762 totalAdvance += advance; | |
763 } | |
764 currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0); | |
765 m_totalWidth += currentRun->width(); | |
766 } | |
767 | |
768 void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, Ha
rfBuzzRun* currentRun, FloatPoint& firstOffsetOfNextRun) | |
769 { | |
770 FloatPoint* offsets = currentRun->offsets(); | |
771 uint16_t* glyphs = currentRun->glyphs(); | |
772 float* advances = currentRun->advances(); | |
773 unsigned numGlyphs = currentRun->numGlyphs(); | |
774 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); | |
775 | |
776 for (unsigned i = 0; i < numGlyphs; ++i) { | |
777 uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToChara
cterIndexes[i]; | |
778 FloatPoint& currentOffset = offsets[i]; | |
779 FloatPoint& nextOffset = (i == numGlyphs - 1) ? firstOffsetOfNextRun : o
ffsets[i + 1]; | |
780 float glyphAdvanceX = advances[i] + nextOffset.x() - currentOffset.x(); | |
781 float glyphAdvanceY = nextOffset.y() - currentOffset.y(); | |
782 if (m_run.rtl()) { | |
783 if (currentCharacterIndex >= m_toIndex) | |
784 m_startOffset.move(glyphAdvanceX, glyphAdvanceY); | |
785 else if (currentCharacterIndex >= m_fromIndex) | |
786 glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphB
ufferAdvance(glyphAdvanceX, glyphAdvanceY)); | |
787 } else { | |
788 if (currentCharacterIndex < m_fromIndex) | |
789 m_startOffset.move(glyphAdvanceX, glyphAdvanceY); | |
790 else if (currentCharacterIndex < m_toIndex) | |
791 glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphB
ufferAdvance(glyphAdvanceX, glyphAdvanceY)); | |
792 } | |
793 } | |
794 } | |
795 | |
796 bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer) | |
797 { | |
798 unsigned numRuns = m_harfBuzzRuns.size(); | |
799 if (m_run.rtl()) { | |
800 m_startOffset = m_harfBuzzRuns.last()->offsets()[0]; | |
801 for (int runIndex = numRuns - 1; runIndex >= 0; --runIndex) { | |
802 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); | |
803 FloatPoint firstOffsetOfNextRun = !runIndex ? FloatPoint() : m_harfB
uzzRuns[runIndex - 1]->offsets()[0]; | |
804 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetO
fNextRun); | |
805 } | |
806 } else { | |
807 m_startOffset = m_harfBuzzRuns.first()->offsets()[0]; | |
808 for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) { | |
809 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); | |
810 FloatPoint firstOffsetOfNextRun = runIndex == numRuns - 1 ? FloatPoi
nt() : m_harfBuzzRuns[runIndex + 1]->offsets()[0]; | |
811 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetO
fNextRun); | |
812 } | |
813 } | |
814 return glyphBuffer->size(); | |
815 } | |
816 | |
817 int HarfBuzzShaper::offsetForPosition(float targetX) | |
818 { | |
819 int charactersSoFar = 0; | |
820 float currentX = 0; | |
821 | |
822 if (m_run.rtl()) { | |
823 charactersSoFar = m_normalizedBufferLength; | |
824 for (int i = m_harfBuzzRuns.size() - 1; i >= 0; --i) { | |
825 charactersSoFar -= m_harfBuzzRuns[i]->numCharacters(); | |
826 float nextX = currentX + m_harfBuzzRuns[i]->width(); | |
827 float offsetForRun = targetX - currentX; | |
828 if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width())
{ | |
829 // The x value in question is within this script run. | |
830 const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosi
tion(offsetForRun); | |
831 return charactersSoFar + index; | |
832 } | |
833 currentX = nextX; | |
834 } | |
835 } else { | |
836 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { | |
837 float nextX = currentX + m_harfBuzzRuns[i]->width(); | |
838 float offsetForRun = targetX - currentX; | |
839 if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width())
{ | |
840 const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosi
tion(offsetForRun); | |
841 return charactersSoFar + index; | |
842 } | |
843 charactersSoFar += m_harfBuzzRuns[i]->numCharacters(); | |
844 currentX = nextX; | |
845 } | |
846 } | |
847 | |
848 return charactersSoFar; | |
849 } | |
850 | |
851 FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int
from, int to) | |
852 { | |
853 float currentX = 0; | |
854 float fromX = 0; | |
855 float toX = 0; | |
856 bool foundFromX = false; | |
857 bool foundToX = false; | |
858 | |
859 if (m_run.rtl()) | |
860 currentX = m_totalWidth; | |
861 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { | |
862 if (m_run.rtl()) | |
863 currentX -= m_harfBuzzRuns[i]->width(); | |
864 int numCharacters = m_harfBuzzRuns[i]->numCharacters(); | |
865 if (!foundFromX && from >= 0 && from < numCharacters) { | |
866 fromX = m_harfBuzzRuns[i]->xPositionForOffset(from) + currentX; | |
867 foundFromX = true; | |
868 } else | |
869 from -= numCharacters; | |
870 | |
871 if (!foundToX && to >= 0 && to < numCharacters) { | |
872 toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX; | |
873 foundToX = true; | |
874 } else | |
875 to -= numCharacters; | |
876 | |
877 if (foundFromX && foundToX) | |
878 break; | |
879 if (!m_run.rtl()) | |
880 currentX += m_harfBuzzRuns[i]->width(); | |
881 } | |
882 | |
883 // The position in question might be just after the text. | |
884 if (!foundFromX) | |
885 fromX = 0; | |
886 if (!foundToX) | |
887 toX = m_run.rtl() ? 0 : m_totalWidth; | |
888 | |
889 // Using floorf() and roundf() as the same as mac port. | |
890 if (fromX < toX) | |
891 return FloatRect(floorf(point.x() + fromX), point.y(), roundf(toX - from
X), height); | |
892 return FloatRect(floorf(point.x() + toX), point.y(), roundf(fromX - toX), he
ight); | |
893 } | |
894 | |
895 } // namespace WebCore | |
OLD | NEW |