OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2012 Google Inc. All rights reserved. | 2 * Copyright (c) 2012 Google Inc. All rights reserved. |
3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. | 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions are | 6 * modification, are permitted provided that the following conditions are |
7 * met: | 7 * met: |
8 * | 8 * |
9 * * Redistributions of source code must retain the above copyright | 9 * * Redistributions of source code must retain the above copyright |
10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
(...skipping 22 matching lines...) Expand all Loading... | |
33 | 33 |
34 #include "platform/fonts/Font.h" | 34 #include "platform/fonts/Font.h" |
35 #include "platform/fonts/FontDescription.h" | 35 #include "platform/fonts/FontDescription.h" |
36 #include "platform/fonts/FontFallbackIterator.h" | 36 #include "platform/fonts/FontFallbackIterator.h" |
37 #include "platform/fonts/GlyphBuffer.h" | 37 #include "platform/fonts/GlyphBuffer.h" |
38 #include "platform/fonts/SmallCapsIterator.h" | 38 #include "platform/fonts/SmallCapsIterator.h" |
39 #include "platform/fonts/UTF16TextIterator.h" | 39 #include "platform/fonts/UTF16TextIterator.h" |
40 #include "platform/fonts/opentype/OpenTypeCapsSupport.h" | 40 #include "platform/fonts/opentype/OpenTypeCapsSupport.h" |
41 #include "platform/fonts/shaping/CaseMappingHarfBuzzBufferFiller.h" | 41 #include "platform/fonts/shaping/CaseMappingHarfBuzzBufferFiller.h" |
42 #include "platform/fonts/shaping/HarfBuzzFace.h" | 42 #include "platform/fonts/shaping/HarfBuzzFace.h" |
43 #include "platform/fonts/shaping/RunSegmenter.h" | |
44 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" | 43 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" |
45 #include "platform/text/TextBreakIterator.h" | 44 #include "platform/text/TextBreakIterator.h" |
46 #include "wtf/Compiler.h" | 45 #include "wtf/Compiler.h" |
47 #include "wtf/MathExtras.h" | 46 #include "wtf/MathExtras.h" |
48 #include "wtf/PtrUtil.h" | 47 #include "wtf/PtrUtil.h" |
49 #include "wtf/text/Unicode.h" | 48 #include "wtf/text/Unicode.h" |
50 #include <algorithm> | 49 #include <algorithm> |
51 #include <hb.h> | 50 #include <hb.h> |
52 #include <memory> | 51 #include <memory> |
53 #include <unicode/uchar.h> | 52 #include <unicode/uchar.h> |
54 #include <unicode/uscript.h> | 53 #include <unicode/uscript.h> |
55 | 54 |
56 namespace blink { | 55 namespace blink { |
57 using FeaturesVector = Vector<hb_feature_t, 6>; | 56 using FeaturesVector = Vector<hb_feature_t, 6>; |
58 | 57 |
58 enum HolesQueueItemAction { HolesQueueNextFont, HolesQueueRange }; | |
59 | |
60 struct HolesQueueItem { | |
61 DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); | |
62 HolesQueueItemAction m_action; | |
63 unsigned m_startIndex; | |
64 unsigned m_numCharacters; | |
65 HolesQueueItem(HolesQueueItemAction action, unsigned start, unsigned num) | |
66 : m_action(action), m_startIndex(start), m_numCharacters(num){}; | |
67 }; | |
68 | |
59 template <typename T> | 69 template <typename T> |
60 class HarfBuzzScopedPtr { | 70 class HarfBuzzScopedPtr { |
61 STACK_ALLOCATED(); | 71 STACK_ALLOCATED(); |
62 WTF_MAKE_NONCOPYABLE(HarfBuzzScopedPtr); | 72 WTF_MAKE_NONCOPYABLE(HarfBuzzScopedPtr); |
63 | 73 |
64 public: | 74 public: |
65 typedef void (*DestroyFunction)(T*); | 75 typedef void (*DestroyFunction)(T*); |
66 | 76 |
67 HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy) | 77 HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy) |
68 : m_ptr(ptr), m_destroy(destroy) { | 78 : m_ptr(ptr), m_destroy(destroy) { |
69 ASSERT(m_destroy); | 79 ASSERT(m_destroy); |
70 } | 80 } |
71 ~HarfBuzzScopedPtr() { | 81 ~HarfBuzzScopedPtr() { |
72 if (m_ptr) | 82 if (m_ptr) |
73 (*m_destroy)(m_ptr); | 83 (*m_destroy)(m_ptr); |
74 } | 84 } |
75 | 85 |
76 T* get() { return m_ptr; } | 86 T* get() { return m_ptr; } |
77 void set(T* ptr) { m_ptr = ptr; } | 87 void set(T* ptr) { m_ptr = ptr; } |
78 | 88 |
79 private: | 89 private: |
80 T* m_ptr; | 90 T* m_ptr; |
81 DestroyFunction m_destroy; | 91 DestroyFunction m_destroy; |
82 }; | 92 }; |
83 | 93 |
84 HarfBuzzShaper::HarfBuzzShaper(const UChar* text, | 94 HarfBuzzShaper::HarfBuzzShaper(const UChar* text, |
85 unsigned length, | 95 unsigned length, |
86 TextDirection direction) | 96 TextDirection direction) |
87 : m_normalizedBuffer(text), | 97 : m_text(text), m_textLength(length), m_paragraphDirection(direction) {} |
88 m_normalizedBufferLength(length), | |
89 m_textDirection(direction) {} | |
90 | 98 |
91 namespace { | 99 namespace { |
92 | 100 |
93 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built | 101 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built |
94 // without hb-icu. See http://crbug.com/356929 | 102 // without hb-icu. See http://crbug.com/356929 |
95 static inline hb_script_t ICUScriptToHBScript(UScriptCode script) { | 103 static inline hb_script_t ICUScriptToHBScript(UScriptCode script) { |
96 if (UNLIKELY(script == USCRIPT_INVALID_CODE)) | 104 if (UNLIKELY(script == USCRIPT_INVALID_CODE)) |
97 return HB_SCRIPT_INVALID; | 105 return HB_SCRIPT_INVALID; |
98 | 106 |
99 return hb_script_from_string(uscript_getShortName(script), -1); | 107 return hb_script_from_string(uscript_getShortName(script), -1); |
100 } | 108 } |
101 | 109 |
102 static inline hb_direction_t TextDirectionToHBDirection( | 110 static inline hb_direction_t TextDirectionToHBDirection( |
103 TextDirection dir, | 111 TextDirection dir, |
104 FontOrientation orientation, | 112 FontOrientation orientation, |
105 const SimpleFontData* fontData) { | 113 const SimpleFontData* fontData) { |
106 hb_direction_t harfBuzzDirection = | 114 hb_direction_t harfBuzzDirection = |
107 isVerticalAnyUpright(orientation) && | 115 isVerticalAnyUpright(orientation) && |
108 !fontData->isTextOrientationFallback() | 116 !fontData->isTextOrientationFallback() |
109 ? HB_DIRECTION_TTB | 117 ? HB_DIRECTION_TTB |
110 : HB_DIRECTION_LTR; | 118 : HB_DIRECTION_LTR; |
111 return dir == TextDirection::kRtl ? HB_DIRECTION_REVERSE(harfBuzzDirection) | 119 return dir == TextDirection::kRtl ? HB_DIRECTION_REVERSE(harfBuzzDirection) |
112 : harfBuzzDirection; | 120 : harfBuzzDirection; |
113 } | 121 } |
114 | 122 |
115 inline bool shapeRange(hb_buffer_t* harfBuzzBuffer, | 123 inline bool shapeRange(hb_buffer_t* buffer, |
116 hb_feature_t* fontFeatures, | 124 hb_feature_t* fontFeatures, |
117 unsigned fontFeaturesSize, | 125 unsigned fontFeaturesSize, |
118 const SimpleFontData* currentFont, | 126 const SimpleFontData* currentFont, |
119 PassRefPtr<UnicodeRangeSet> currentFontRangeSet, | 127 PassRefPtr<UnicodeRangeSet> currentFontRangeSet, |
120 UScriptCode currentRunScript, | 128 UScriptCode currentRunScript, |
121 hb_direction_t direction, | 129 hb_direction_t direction, |
122 hb_language_t language) { | 130 hb_language_t language) { |
123 const FontPlatformData* platformData = &(currentFont->platformData()); | 131 const FontPlatformData* platformData = &(currentFont->platformData()); |
124 HarfBuzzFace* face = platformData->harfBuzzFace(); | 132 HarfBuzzFace* face = platformData->harfBuzzFace(); |
125 if (!face) { | 133 if (!face) { |
126 DLOG(ERROR) << "Could not create HarfBuzzFace from FontPlatformData."; | 134 DLOG(ERROR) << "Could not create HarfBuzzFace from FontPlatformData."; |
127 return false; | 135 return false; |
128 } | 136 } |
129 | 137 |
130 hb_buffer_set_language(harfBuzzBuffer, language); | 138 hb_buffer_set_language(buffer, language); |
131 hb_buffer_set_script(harfBuzzBuffer, ICUScriptToHBScript(currentRunScript)); | 139 hb_buffer_set_script(buffer, ICUScriptToHBScript(currentRunScript)); |
132 hb_buffer_set_direction(harfBuzzBuffer, direction); | 140 hb_buffer_set_direction(buffer, direction); |
133 | 141 |
134 hb_font_t* hbFont = face->getScaledFont(std::move(currentFontRangeSet)); | 142 hb_font_t* hbFont = face->getScaledFont(std::move(currentFontRangeSet)); |
135 hb_shape(hbFont, harfBuzzBuffer, fontFeatures, fontFeaturesSize); | 143 hb_shape(hbFont, buffer, fontFeatures, fontFeaturesSize); |
136 | 144 |
137 return true; | 145 return true; |
138 } | 146 } |
139 | 147 |
140 } // namespace | 148 } // namespace |
141 | 149 |
142 bool HarfBuzzShaper::extractShapeResults(hb_buffer_t* harfBuzzBuffer, | 150 bool HarfBuzzShaper::extractShapeResults(hb_buffer_t* buffer, |
143 ShapeResult* shapeResult, | 151 ShapeResult* shapeResult, |
144 bool& fontCycleQueued, | 152 bool& fontCycleQueued, |
145 Deque<HolesQueueItem>* holesQueue, | 153 Deque<HolesQueueItem>* holesQueue, |
146 const HolesQueueItem& currentQueueItem, | 154 const HolesQueueItem& currentQueueItem, |
147 const Font* font, | 155 const Font* font, |
148 const SimpleFontData* currentFont, | 156 const SimpleFontData* currentFont, |
149 UScriptCode currentRunScript, | 157 UScriptCode currentRunScript, |
150 bool isLastResort) const { | 158 bool isLastResort) const { |
151 enum ClusterResult { Shaped, NotDef, Unknown }; | 159 enum ClusterResult { Shaped, NotDef, Unknown }; |
152 ClusterResult currentClusterResult = Unknown; | 160 ClusterResult currentClusterResult = Unknown; |
153 ClusterResult previousClusterResult = Unknown; | 161 ClusterResult previousClusterResult = Unknown; |
154 unsigned previousCluster = 0; | 162 unsigned previousCluster = 0; |
155 unsigned currentCluster = 0; | 163 unsigned currentCluster = 0; |
156 | 164 |
157 // Find first notdef glyph in harfBuzzBuffer. | 165 // Find first notdef glyph in buffer. |
158 unsigned numGlyphs = hb_buffer_get_length(harfBuzzBuffer); | 166 unsigned numGlyphs = hb_buffer_get_length(buffer); |
159 hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); | 167 hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos(buffer, 0); |
160 | 168 |
161 unsigned lastChangePosition = 0; | 169 unsigned lastChangePosition = 0; |
162 | 170 |
163 if (!numGlyphs) { | 171 if (!numGlyphs) { |
164 DLOG(ERROR) << "HarfBuzz returned empty glyph buffer after shaping."; | 172 DLOG(ERROR) << "HarfBuzz returned empty glyph buffer after shaping."; |
165 return false; | 173 return false; |
166 } | 174 } |
167 | 175 |
168 for (unsigned glyphIndex = 0; glyphIndex <= numGlyphs; ++glyphIndex) { | 176 for (unsigned glyphIndex = 0; glyphIndex <= numGlyphs; ++glyphIndex) { |
169 // Iterating by clusters, check for when the state switches from shaped | 177 // Iterating by clusters, check for when the state switches from shaped |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
205 previousClusterResult != Unknown; | 213 previousClusterResult != Unknown; |
206 if (!atChange) | 214 if (!atChange) |
207 continue; | 215 continue; |
208 | 216 |
209 // Compute the range indices of consecutive shaped or .notdef glyphs. | 217 // Compute the range indices of consecutive shaped or .notdef glyphs. |
210 // Cluster information for RTL runs becomes reversed, e.g. character 0 | 218 // Cluster information for RTL runs becomes reversed, e.g. character 0 |
211 // has cluster index 5 in a run of 6 characters. | 219 // has cluster index 5 in a run of 6 characters. |
212 unsigned numCharacters = 0; | 220 unsigned numCharacters = 0; |
213 unsigned numGlyphsToInsert = 0; | 221 unsigned numGlyphsToInsert = 0; |
214 unsigned startIndex = 0; | 222 unsigned startIndex = 0; |
215 if (HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(harfBuzzBuffer))) { | 223 if (HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(buffer))) { |
216 startIndex = glyphInfo[lastChangePosition].cluster; | 224 startIndex = glyphInfo[lastChangePosition].cluster; |
217 if (glyphIndex == numGlyphs) { | 225 if (glyphIndex == numGlyphs) { |
218 numCharacters = currentQueueItem.m_startIndex + | 226 numCharacters = currentQueueItem.m_startIndex + |
219 currentQueueItem.m_numCharacters - | 227 currentQueueItem.m_numCharacters - |
220 glyphInfo[lastChangePosition].cluster; | 228 glyphInfo[lastChangePosition].cluster; |
221 numGlyphsToInsert = numGlyphs - lastChangePosition; | 229 numGlyphsToInsert = numGlyphs - lastChangePosition; |
222 } else { | 230 } else { |
223 numCharacters = glyphInfo[glyphIndex].cluster - | 231 numCharacters = glyphInfo[glyphIndex].cluster - |
224 glyphInfo[lastChangePosition].cluster; | 232 glyphInfo[lastChangePosition].cluster; |
225 numGlyphsToInsert = glyphIndex - lastChangePosition; | 233 numGlyphsToInsert = glyphIndex - lastChangePosition; |
(...skipping 27 matching lines...) Expand all Loading... | |
253 | 261 |
254 // If numCharacters is 0, that means we hit a NotDef before shaping the | 262 // If numCharacters is 0, that means we hit a NotDef before shaping the |
255 // whole grapheme. We do not append it here. For the next glyph we | 263 // whole grapheme. We do not append it here. For the next glyph we |
256 // encounter, atChange will be true, and the characters corresponding to | 264 // encounter, atChange will be true, and the characters corresponding to |
257 // the grapheme will be added to the TODO queue again, attempting to | 265 // the grapheme will be added to the TODO queue again, attempting to |
258 // shape the whole grapheme with the next font. | 266 // shape the whole grapheme with the next font. |
259 // When we're getting here with the last resort font, we have no other | 267 // When we're getting here with the last resort font, we have no other |
260 // choice than adding boxes to the ShapeResult. | 268 // choice than adding boxes to the ShapeResult. |
261 if ((currentClusterResult == NotDef && numCharacters) || isLastResort) { | 269 if ((currentClusterResult == NotDef && numCharacters) || isLastResort) { |
262 hb_direction_t direction = TextDirectionToHBDirection( | 270 hb_direction_t direction = TextDirectionToHBDirection( |
263 m_textDirection, font->getFontDescription().orientation(), | 271 m_paragraphDirection, font->getFontDescription().orientation(), |
264 currentFont); | 272 currentFont); |
265 // Here we need to specify glyph positions. | 273 // Here we need to specify glyph positions. |
266 ShapeResult::RunInfo* run = new ShapeResult::RunInfo( | 274 ShapeResult::RunInfo* run = new ShapeResult::RunInfo( |
267 currentFont, direction, ICUScriptToHBScript(currentRunScript), | 275 currentFont, direction, ICUScriptToHBScript(currentRunScript), |
268 startIndex, numGlyphsToInsert, numCharacters); | 276 startIndex, numGlyphsToInsert, numCharacters); |
269 shapeResult->insertRun(WTF::wrapUnique(run), lastChangePosition, | 277 shapeResult->insertRun(WTF::wrapUnique(run), lastChangePosition, |
270 numGlyphsToInsert, harfBuzzBuffer); | 278 numGlyphsToInsert, buffer); |
271 } | 279 } |
272 lastChangePosition = glyphIndex; | 280 lastChangePosition = glyphIndex; |
273 } | 281 } |
274 return true; | 282 return true; |
275 } | 283 } |
276 | 284 |
277 static inline const SimpleFontData* fontDataAdjustedForOrientation( | 285 static inline const SimpleFontData* fontDataAdjustedForOrientation( |
278 const SimpleFontData* originalFont, | 286 const SimpleFontData* originalFont, |
279 FontOrientation runOrientation, | 287 FontOrientation runOrientation, |
280 OrientationIterator::RenderOrientation renderOrientation) { | 288 OrientationIterator::RenderOrientation renderOrientation) { |
281 if (!isVerticalBaseline(runOrientation)) | 289 if (!isVerticalBaseline(runOrientation)) |
282 return originalFont; | 290 return originalFont; |
283 | 291 |
284 if (runOrientation == FontOrientation::VerticalRotated || | 292 if (runOrientation == FontOrientation::VerticalRotated || |
285 (runOrientation == FontOrientation::VerticalMixed && | 293 (runOrientation == FontOrientation::VerticalMixed && |
286 renderOrientation == OrientationIterator::OrientationRotateSideways)) | 294 renderOrientation == OrientationIterator::OrientationRotateSideways)) |
287 return originalFont->verticalRightOrientationFontData().get(); | 295 return originalFont->verticalRightOrientationFontData().get(); |
288 | 296 |
289 return originalFont; | 297 return originalFont; |
290 } | 298 } |
291 | 299 |
292 bool HarfBuzzShaper::collectFallbackHintChars( | 300 bool HarfBuzzShaper::collectFallbackHintChars( |
293 const Deque<HolesQueueItem>& holesQueue, | 301 const Deque<HolesQueueItem>* holesQueue, |
294 Vector<UChar32>& hint) const { | 302 Vector<UChar32>& hint) const { |
295 if (!holesQueue.size()) | 303 DCHECK(holesQueue); |
304 if (!holesQueue->size()) | |
296 return false; | 305 return false; |
297 | 306 |
298 hint.clear(); | 307 hint.clear(); |
299 | 308 |
300 size_t numCharsAdded = 0; | 309 size_t numCharsAdded = 0; |
301 for (auto it = holesQueue.begin(); it != holesQueue.end(); ++it) { | 310 for (auto it = holesQueue->begin(); it != holesQueue->end(); ++it) { |
302 if (it->m_action == HolesQueueNextFont) | 311 if (it->m_action == HolesQueueNextFont) |
303 break; | 312 break; |
304 | 313 |
305 UChar32 hintChar; | 314 UChar32 hintChar; |
306 RELEASE_ASSERT(it->m_startIndex + it->m_numCharacters <= | 315 RELEASE_ASSERT(it->m_startIndex + it->m_numCharacters <= m_textLength); |
307 m_normalizedBufferLength); | 316 UTF16TextIterator iterator(m_text + it->m_startIndex, it->m_numCharacters); |
308 UTF16TextIterator iterator(m_normalizedBuffer + it->m_startIndex, | |
309 it->m_numCharacters); | |
310 while (iterator.consume(hintChar)) { | 317 while (iterator.consume(hintChar)) { |
311 hint.push_back(hintChar); | 318 hint.push_back(hintChar); |
312 numCharsAdded++; | 319 numCharsAdded++; |
313 iterator.advance(); | 320 iterator.advance(); |
314 } | 321 } |
315 } | 322 } |
316 return numCharsAdded > 0; | 323 return numCharsAdded > 0; |
317 } | 324 } |
318 | 325 |
319 namespace { | 326 namespace { |
320 using HolesQueueItem = HarfBuzzShaper::HolesQueueItem; | |
321 using HolesQueueItemAction = HarfBuzzShaper::HolesQueueItemAction; | |
322 | 327 |
323 void splitUntilNextCaseChange( | 328 void splitUntilNextCaseChange( |
324 const UChar* normalizedBuffer, | 329 const UChar* normalizedBuffer, |
325 Deque<HolesQueueItem>* queue, | 330 Deque<blink::HolesQueueItem>* queue, |
326 HolesQueueItem& currentQueueItem, | 331 blink::HolesQueueItem& currentQueueItem, |
327 SmallCapsIterator::SmallCapsBehavior& smallCapsBehavior) { | 332 SmallCapsIterator::SmallCapsBehavior& smallCapsBehavior) { |
328 unsigned numCharactersUntilCaseChange = 0; | 333 unsigned numCharactersUntilCaseChange = 0; |
329 SmallCapsIterator smallCapsIterator( | 334 SmallCapsIterator smallCapsIterator( |
330 normalizedBuffer + currentQueueItem.m_startIndex, | 335 normalizedBuffer + currentQueueItem.m_startIndex, |
331 currentQueueItem.m_numCharacters); | 336 currentQueueItem.m_numCharacters); |
332 smallCapsIterator.consume(&numCharactersUntilCaseChange, &smallCapsBehavior); | 337 smallCapsIterator.consume(&numCharactersUntilCaseChange, &smallCapsBehavior); |
333 if (numCharactersUntilCaseChange > 0 && | 338 if (numCharactersUntilCaseChange > 0 && |
334 numCharactersUntilCaseChange < currentQueueItem.m_numCharacters) { | 339 numCharactersUntilCaseChange < currentQueueItem.m_numCharacters) { |
335 queue->prepend(HolesQueueItem( | 340 queue->prepend(blink::HolesQueueItem( |
336 HolesQueueItemAction::HolesQueueRange, | 341 blink::HolesQueueItemAction::HolesQueueRange, |
337 currentQueueItem.m_startIndex + numCharactersUntilCaseChange, | 342 currentQueueItem.m_startIndex + numCharactersUntilCaseChange, |
338 currentQueueItem.m_numCharacters - numCharactersUntilCaseChange)); | 343 currentQueueItem.m_numCharacters - numCharactersUntilCaseChange)); |
339 currentQueueItem.m_numCharacters = numCharactersUntilCaseChange; | 344 currentQueueItem.m_numCharacters = numCharactersUntilCaseChange; |
340 } | 345 } |
341 } | 346 } |
342 | 347 |
343 hb_feature_t createFeature(hb_tag_t tag, uint32_t value = 0) { | 348 hb_feature_t createFeature(hb_tag_t tag, uint32_t value = 0) { |
344 return {tag, value, 0 /* start */, static_cast<unsigned>(-1) /* end */}; | 349 return {tag, value, 0 /* start */, static_cast<unsigned>(-1) /* end */}; |
345 } | 350 } |
346 | 351 |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
539 m_features->prepend(feature); | 544 m_features->prepend(feature); |
540 m_countFeatures++; | 545 m_countFeatures++; |
541 } | 546 } |
542 | 547 |
543 CapsFeatureSettingsScopedOverlay::~CapsFeatureSettingsScopedOverlay() { | 548 CapsFeatureSettingsScopedOverlay::~CapsFeatureSettingsScopedOverlay() { |
544 m_features->remove(0, m_countFeatures); | 549 m_features->remove(0, m_countFeatures); |
545 } | 550 } |
546 | 551 |
547 } // namespace | 552 } // namespace |
548 | 553 |
549 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeResult(const Font* font) const { | 554 void HarfBuzzShaper::shapeSegment(ShapeResult* result, |
550 RefPtr<ShapeResult> result = | 555 Deque<HolesQueueItem>* holesQueue, |
551 ShapeResult::create(font, m_normalizedBufferLength, m_textDirection); | 556 hb_buffer_t* buffer, |
552 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), | 557 const Font* font, |
553 hb_buffer_destroy); | 558 FeaturesVector* fontFeatures, |
554 FeaturesVector fontFeatures; | 559 RunSegmenter::RunSegmenterRange segment, |
555 setFontFeatures(font, &fontFeatures); | 560 unsigned start, |
561 unsigned end) const { | |
562 DCHECK(result); | |
563 DCHECK(holesQueue); | |
564 DCHECK(buffer); | |
565 | |
556 const FontDescription& fontDescription = font->getFontDescription(); | 566 const FontDescription& fontDescription = font->getFontDescription(); |
557 const hb_language_t language = | 567 const hb_language_t language = |
558 fontDescription.localeOrDefault().harfbuzzLanguage(); | 568 fontDescription.localeOrDefault().harfbuzzLanguage(); |
559 | |
560 bool needsCapsHandling = | 569 bool needsCapsHandling = |
561 fontDescription.variantCaps() != FontDescription::CapsNormal; | 570 fontDescription.variantCaps() != FontDescription::CapsNormal; |
562 OpenTypeCapsSupport capsSupport; | 571 OpenTypeCapsSupport capsSupport; |
572 | |
573 FontOrientation orientation = font->getFontDescription().orientation(); | |
574 RefPtr<FontFallbackIterator> fallbackIterator = | |
575 font->createFontFallbackIterator(segment.fontFallbackPriority); | |
576 | |
577 holesQueue->append(HolesQueueItem(HolesQueueNextFont, 0, 0)); | |
578 holesQueue->append(HolesQueueItem(HolesQueueRange, segment.start, | |
579 segment.end - segment.start)); | |
580 | |
581 bool fontCycleQueued = false; | |
582 Vector<UChar32> fallbackCharsHint; | |
583 RefPtr<FontDataForRangeSet> currentFontDataForRangeSet; | |
584 while (holesQueue->size()) { | |
585 HolesQueueItem currentQueueItem = holesQueue->takeFirst(); | |
586 | |
587 if (currentQueueItem.m_action == HolesQueueNextFont) { | |
588 // For now, we're building a character list with which we probe | |
589 // for needed fonts depending on the declared unicode-range of a | |
590 // segmented CSS font. Alternatively, we can build a fake font | |
591 // for the shaper and check whether any glyphs were found, or | |
592 // define a new API on the shaper which will give us coverage | |
593 // information? | |
594 if (!collectFallbackHintChars(holesQueue, fallbackCharsHint)) { | |
595 // Give up shaping since we cannot retrieve a font fallback | |
596 // font without a hintlist. | |
597 holesQueue->clear(); | |
598 break; | |
599 } | |
600 | |
601 currentFontDataForRangeSet = fallbackIterator->next(fallbackCharsHint); | |
602 if (!currentFontDataForRangeSet->fontData()) { | |
603 DCHECK(!holesQueue->size()); | |
604 break; | |
605 } | |
606 fontCycleQueued = false; | |
607 continue; | |
608 } | |
609 | |
610 const SimpleFontData* fontData = currentFontDataForRangeSet->fontData(); | |
611 SmallCapsIterator::SmallCapsBehavior smallCapsBehavior = | |
612 SmallCapsIterator::SmallCapsSameCase; | |
613 if (needsCapsHandling) { | |
614 capsSupport = OpenTypeCapsSupport(fontData->platformData().harfBuzzFace(), | |
615 fontDescription.variantCaps(), | |
616 ICUScriptToHBScript(segment.script)); | |
617 if (capsSupport.needsRunCaseSplitting()) { | |
618 splitUntilNextCaseChange(m_text, holesQueue, currentQueueItem, | |
619 smallCapsBehavior); | |
620 } | |
621 } | |
622 | |
623 DCHECK(currentQueueItem.m_numCharacters); | |
624 const SimpleFontData* smallcapsAdjustedFont = | |
625 needsCapsHandling && capsSupport.needsSyntheticFont(smallCapsBehavior) | |
626 ? fontData->smallCapsFontData(fontDescription).get() | |
627 : fontData; | |
628 | |
629 // Compatibility with SimpleFontData approach of keeping a flag for | |
630 // overriding drawing direction. | |
631 // TODO: crbug.com/506224 This should go away in favor of storing that | |
632 // information elsewhere, for example in ShapeResult. | |
633 const SimpleFontData* directionAndSmallCapsAdjustedFont = | |
634 fontDataAdjustedForOrientation(smallcapsAdjustedFont, orientation, | |
635 segment.renderOrientation); | |
636 | |
637 CaseMapIntend caseMapIntend = CaseMapIntend::KeepSameCase; | |
638 if (needsCapsHandling) | |
639 caseMapIntend = capsSupport.needsCaseChange(smallCapsBehavior); | |
640 | |
641 // Clamp the start and end offsets of the queue item to the offsets | |
drott
2017/02/08 15:52:59
What do you anticipate these shapeStart and shapeE
| |
642 // representing the shaping window. | |
643 unsigned shapeStart = std::max(start, currentQueueItem.m_startIndex); | |
644 unsigned shapeEnd = std::min( | |
645 end, currentQueueItem.m_startIndex + currentQueueItem.m_numCharacters); | |
646 | |
647 CaseMappingHarfBuzzBufferFiller( | |
648 caseMapIntend, fontDescription.localeOrDefault(), buffer, m_text, | |
649 m_textLength, shapeStart, shapeEnd - shapeStart); | |
650 | |
651 CapsFeatureSettingsScopedOverlay capsOverlay( | |
652 fontFeatures, capsSupport.fontFeatureToUse(smallCapsBehavior)); | |
653 hb_direction_t direction = TextDirectionToHBDirection( | |
654 m_paragraphDirection, orientation, directionAndSmallCapsAdjustedFont); | |
655 | |
656 if (!shapeRange(buffer, fontFeatures->isEmpty() ? 0 : fontFeatures->data(), | |
657 fontFeatures->size(), directionAndSmallCapsAdjustedFont, | |
658 currentFontDataForRangeSet->ranges(), segment.script, | |
659 direction, language)) | |
660 DLOG(ERROR) << "Shaping range failed."; | |
661 | |
662 if (!extractShapeResults(buffer, result, fontCycleQueued, holesQueue, | |
663 currentQueueItem, font, | |
664 directionAndSmallCapsAdjustedFont, segment.script, | |
665 !fallbackIterator->hasNext())) | |
666 DLOG(ERROR) << "Shape result extraction failed."; | |
667 | |
668 hb_buffer_reset(buffer); | |
669 } | |
670 } | |
671 | |
672 PassRefPtr<ShapeResult> HarfBuzzShaper::shape(const Font* font, | |
673 unsigned start, | |
674 unsigned end) const { | |
675 DCHECK(end >= start); | |
676 DCHECK(end <= m_textLength); | |
677 | |
678 unsigned length = end - start; | |
679 RefPtr<ShapeResult> result = | |
680 ShapeResult::create(font, length, m_paragraphDirection); | |
681 HarfBuzzScopedPtr<hb_buffer_t> buffer(hb_buffer_create(), hb_buffer_destroy); | |
682 | |
683 FeaturesVector fontFeatures; | |
684 setFontFeatures(font, &fontFeatures); | |
563 FontOrientation orientation = font->getFontDescription().orientation(); | 685 FontOrientation orientation = font->getFontDescription().orientation(); |
564 | 686 |
565 RunSegmenter::RunSegmenterRange segmentRange = { | 687 // Run segmentation needs to operate on the entire string, regardless of the |
566 0, 0, USCRIPT_INVALID_CODE, OrientationIterator::OrientationInvalid, | 688 // shaping window (defined by the start and end parameters). |
567 FontFallbackPriority::Invalid}; | 689 RunSegmenter::RunSegmenterRange segmentRange = RunSegmenter::nullRange(); |
568 RunSegmenter runSegmenter(m_normalizedBuffer, m_normalizedBufferLength, | 690 RunSegmenter runSegmenter(m_text, m_textLength, orientation); |
569 orientation); | |
570 | 691 |
571 Vector<UChar32> fallbackCharsHint; | |
572 | |
573 // TODO: Check whether this treatAsZerowidthspace from the previous script | |
574 // segmentation plays a role here, does the new scriptRuniterator handle that | |
575 // correctly? | |
576 Deque<HolesQueueItem> holesQueue; | 692 Deque<HolesQueueItem> holesQueue; |
577 while (runSegmenter.consume(&segmentRange)) { | 693 while (runSegmenter.consume(&segmentRange)) { |
578 RefPtr<FontFallbackIterator> fallbackIterator = | 694 // Only shape segments within the range indicated by start and end offsets. |
drott
2017/02/08 15:52:59
IIUC the condition currently means: // Only shape
| |
579 font->createFontFallbackIterator(segmentRange.fontFallbackPriority); | 695 if (start < segmentRange.end && end > segmentRange.start) { |
580 | 696 shapeSegment(result.get(), &holesQueue, buffer.get(), font, &fontFeatures, |
581 holesQueue.append(HolesQueueItem(HolesQueueNextFont, 0, 0)); | 697 segmentRange, start, end); |
582 holesQueue.append(HolesQueueItem(HolesQueueRange, segmentRange.start, | |
583 segmentRange.end - segmentRange.start)); | |
584 | |
585 RefPtr<FontDataForRangeSet> currentFontDataForRangeSet; | |
586 | |
587 bool fontCycleQueued = false; | |
588 while (holesQueue.size()) { | |
589 HolesQueueItem currentQueueItem = holesQueue.takeFirst(); | |
590 | |
591 if (currentQueueItem.m_action == HolesQueueNextFont) { | |
592 // For now, we're building a character list with which we probe | |
593 // for needed fonts depending on the declared unicode-range of a | |
594 // segmented CSS font. Alternatively, we can build a fake font | |
595 // for the shaper and check whether any glyphs were found, or | |
596 // define a new API on the shaper which will give us coverage | |
597 // information? | |
598 if (!collectFallbackHintChars(holesQueue, fallbackCharsHint)) { | |
599 // Give up shaping since we cannot retrieve a font fallback | |
600 // font without a hintlist. | |
601 holesQueue.clear(); | |
602 break; | |
603 } | |
604 | |
605 currentFontDataForRangeSet = fallbackIterator->next(fallbackCharsHint); | |
606 if (!currentFontDataForRangeSet->fontData()) { | |
607 DCHECK(!holesQueue.size()); | |
608 break; | |
609 } | |
610 fontCycleQueued = false; | |
611 continue; | |
612 } | |
613 | |
614 const SimpleFontData* fontData = currentFontDataForRangeSet->fontData(); | |
615 SmallCapsIterator::SmallCapsBehavior smallCapsBehavior = | |
616 SmallCapsIterator::SmallCapsSameCase; | |
617 if (needsCapsHandling) { | |
618 capsSupport = | |
619 OpenTypeCapsSupport(fontData->platformData().harfBuzzFace(), | |
620 fontDescription.variantCaps(), | |
621 ICUScriptToHBScript(segmentRange.script)); | |
622 if (capsSupport.needsRunCaseSplitting()) { | |
623 splitUntilNextCaseChange(m_normalizedBuffer, &holesQueue, | |
624 currentQueueItem, smallCapsBehavior); | |
625 } | |
626 } | |
627 | |
628 ASSERT(currentQueueItem.m_numCharacters); | |
629 | |
630 const SimpleFontData* smallcapsAdjustedFont = | |
631 needsCapsHandling && capsSupport.needsSyntheticFont(smallCapsBehavior) | |
632 ? fontData->smallCapsFontData(fontDescription).get() | |
633 : fontData; | |
634 | |
635 // Compatibility with SimpleFontData approach of keeping a flag for | |
636 // overriding drawing direction. | |
637 // TODO: crbug.com/506224 This should go away in favor of storing that | |
638 // information elsewhere, for example in ShapeResult. | |
639 const SimpleFontData* directionAndSmallCapsAdjustedFont = | |
640 fontDataAdjustedForOrientation(smallcapsAdjustedFont, orientation, | |
641 segmentRange.renderOrientation); | |
642 | |
643 CaseMapIntend caseMapIntend = CaseMapIntend::KeepSameCase; | |
644 if (needsCapsHandling) | |
645 caseMapIntend = capsSupport.needsCaseChange(smallCapsBehavior); | |
646 | |
647 CaseMappingHarfBuzzBufferFiller( | |
648 caseMapIntend, fontDescription.localeOrDefault(), | |
649 harfBuzzBuffer.get(), m_normalizedBuffer, m_normalizedBufferLength, | |
650 currentQueueItem.m_startIndex, currentQueueItem.m_numCharacters); | |
651 | |
652 CapsFeatureSettingsScopedOverlay capsOverlay( | |
653 &fontFeatures, capsSupport.fontFeatureToUse(smallCapsBehavior)); | |
654 | |
655 hb_direction_t direction = TextDirectionToHBDirection( | |
656 m_textDirection, orientation, directionAndSmallCapsAdjustedFont); | |
657 | |
658 if (!shapeRange(harfBuzzBuffer.get(), | |
659 fontFeatures.isEmpty() ? 0 : fontFeatures.data(), | |
660 fontFeatures.size(), directionAndSmallCapsAdjustedFont, | |
661 currentFontDataForRangeSet->ranges(), segmentRange.script, | |
662 direction, language)) | |
663 DLOG(ERROR) << "Shaping range failed."; | |
664 | |
665 if (!extractShapeResults( | |
666 harfBuzzBuffer.get(), result.get(), fontCycleQueued, &holesQueue, | |
667 currentQueueItem, font, directionAndSmallCapsAdjustedFont, | |
668 segmentRange.script, !fallbackIterator->hasNext())) | |
669 DLOG(ERROR) << "Shape result extraction failed."; | |
670 | |
671 hb_buffer_reset(harfBuzzBuffer.get()); | |
672 } | 698 } |
673 } | 699 } |
674 return result.release(); | 700 return result.release(); |
675 } | 701 } |
676 | 702 |
703 PassRefPtr<ShapeResult> HarfBuzzShaper::shape(const Font* font) const { | |
704 return shape(font, 0, m_textLength); | |
705 } | |
677 } // namespace blink | 706 } // namespace blink |
OLD | NEW |