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, unsigned length) |
85 unsigned length, | 95 : m_text(text), m_textLength(length) {} |
86 TextDirection direction) | |
87 : m_normalizedBuffer(text), | |
88 m_normalizedBufferLength(length), | |
89 m_textDirection(direction) {} | |
90 | 96 |
91 namespace { | 97 namespace { |
92 | 98 |
93 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built | 99 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built |
94 // without hb-icu. See http://crbug.com/356929 | 100 // without hb-icu. See http://crbug.com/356929 |
95 static inline hb_script_t ICUScriptToHBScript(UScriptCode script) { | 101 static inline hb_script_t ICUScriptToHBScript(UScriptCode script) { |
96 if (UNLIKELY(script == USCRIPT_INVALID_CODE)) | 102 if (UNLIKELY(script == USCRIPT_INVALID_CODE)) |
97 return HB_SCRIPT_INVALID; | 103 return HB_SCRIPT_INVALID; |
98 | 104 |
99 return hb_script_from_string(uscript_getShortName(script), -1); | 105 return hb_script_from_string(uscript_getShortName(script), -1); |
100 } | 106 } |
101 | 107 |
102 static inline hb_direction_t TextDirectionToHBDirection( | 108 static inline hb_direction_t TextDirectionToHBDirection( |
103 TextDirection dir, | 109 TextDirection dir, |
104 FontOrientation orientation, | 110 FontOrientation orientation, |
105 const SimpleFontData* fontData) { | 111 const SimpleFontData* fontData) { |
106 hb_direction_t harfBuzzDirection = | 112 hb_direction_t harfBuzzDirection = |
107 isVerticalAnyUpright(orientation) && | 113 isVerticalAnyUpright(orientation) && |
108 !fontData->isTextOrientationFallback() | 114 !fontData->isTextOrientationFallback() |
109 ? HB_DIRECTION_TTB | 115 ? HB_DIRECTION_TTB |
110 : HB_DIRECTION_LTR; | 116 : HB_DIRECTION_LTR; |
111 return dir == TextDirection::kRtl ? HB_DIRECTION_REVERSE(harfBuzzDirection) | 117 return dir == TextDirection::kRtl ? HB_DIRECTION_REVERSE(harfBuzzDirection) |
112 : harfBuzzDirection; | 118 : harfBuzzDirection; |
113 } | 119 } |
114 | 120 |
115 inline bool shapeRange(hb_buffer_t* harfBuzzBuffer, | 121 inline bool shapeRange(hb_buffer_t* buffer, |
116 hb_feature_t* fontFeatures, | 122 hb_feature_t* fontFeatures, |
117 unsigned fontFeaturesSize, | 123 unsigned fontFeaturesSize, |
118 const SimpleFontData* currentFont, | 124 const SimpleFontData* currentFont, |
119 PassRefPtr<UnicodeRangeSet> currentFontRangeSet, | 125 PassRefPtr<UnicodeRangeSet> currentFontRangeSet, |
120 UScriptCode currentRunScript, | 126 UScriptCode currentRunScript, |
121 hb_direction_t direction, | 127 hb_direction_t direction, |
122 hb_language_t language) { | 128 hb_language_t language) { |
123 const FontPlatformData* platformData = &(currentFont->platformData()); | 129 const FontPlatformData* platformData = &(currentFont->platformData()); |
124 HarfBuzzFace* face = platformData->harfBuzzFace(); | 130 HarfBuzzFace* face = platformData->harfBuzzFace(); |
125 if (!face) { | 131 if (!face) { |
126 DLOG(ERROR) << "Could not create HarfBuzzFace from FontPlatformData."; | 132 DLOG(ERROR) << "Could not create HarfBuzzFace from FontPlatformData."; |
127 return false; | 133 return false; |
128 } | 134 } |
129 | 135 |
130 hb_buffer_set_language(harfBuzzBuffer, language); | 136 hb_buffer_set_language(buffer, language); |
131 hb_buffer_set_script(harfBuzzBuffer, ICUScriptToHBScript(currentRunScript)); | 137 hb_buffer_set_script(buffer, ICUScriptToHBScript(currentRunScript)); |
132 hb_buffer_set_direction(harfBuzzBuffer, direction); | 138 hb_buffer_set_direction(buffer, direction); |
133 | 139 |
134 hb_font_t* hbFont = face->getScaledFont(std::move(currentFontRangeSet)); | 140 hb_font_t* hbFont = face->getScaledFont(std::move(currentFontRangeSet)); |
135 hb_shape(hbFont, harfBuzzBuffer, fontFeatures, fontFeaturesSize); | 141 hb_shape(hbFont, buffer, fontFeatures, fontFeaturesSize); |
136 | 142 |
137 return true; | 143 return true; |
138 } | 144 } |
139 | 145 |
140 } // namespace | 146 } // namespace |
141 | 147 |
142 bool HarfBuzzShaper::extractShapeResults(hb_buffer_t* harfBuzzBuffer, | 148 bool HarfBuzzShaper::extractShapeResults(hb_buffer_t* buffer, |
143 ShapeResult* shapeResult, | 149 ShapeResult* shapeResult, |
144 bool& fontCycleQueued, | 150 bool& fontCycleQueued, |
145 Deque<HolesQueueItem>* holesQueue, | 151 Deque<HolesQueueItem>* holesQueue, |
146 const HolesQueueItem& currentQueueItem, | 152 const HolesQueueItem& currentQueueItem, |
147 const Font* font, | 153 const Font* font, |
154 TextDirection textDirection, | |
148 const SimpleFontData* currentFont, | 155 const SimpleFontData* currentFont, |
149 UScriptCode currentRunScript, | 156 UScriptCode currentRunScript, |
150 bool isLastResort) const { | 157 bool isLastResort) const { |
151 enum ClusterResult { Shaped, NotDef, Unknown }; | 158 enum ClusterResult { Shaped, NotDef, Unknown }; |
152 ClusterResult currentClusterResult = Unknown; | 159 ClusterResult currentClusterResult = Unknown; |
153 ClusterResult previousClusterResult = Unknown; | 160 ClusterResult previousClusterResult = Unknown; |
154 unsigned previousCluster = 0; | 161 unsigned previousCluster = 0; |
155 unsigned currentCluster = 0; | 162 unsigned currentCluster = 0; |
156 | 163 |
157 // Find first notdef glyph in harfBuzzBuffer. | 164 // Find first notdef glyph in buffer. |
158 unsigned numGlyphs = hb_buffer_get_length(harfBuzzBuffer); | 165 unsigned numGlyphs = hb_buffer_get_length(buffer); |
159 hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); | 166 hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos(buffer, 0); |
160 | 167 |
161 unsigned lastChangePosition = 0; | 168 unsigned lastChangePosition = 0; |
162 | 169 |
163 if (!numGlyphs) { | 170 if (!numGlyphs) { |
164 DLOG(ERROR) << "HarfBuzz returned empty glyph buffer after shaping."; | 171 DLOG(ERROR) << "HarfBuzz returned empty glyph buffer after shaping."; |
165 return false; | 172 return false; |
166 } | 173 } |
167 | 174 |
168 for (unsigned glyphIndex = 0; glyphIndex <= numGlyphs; ++glyphIndex) { | 175 for (unsigned glyphIndex = 0; glyphIndex <= numGlyphs; ++glyphIndex) { |
169 // Iterating by clusters, check for when the state switches from shaped | 176 // 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; | 212 previousClusterResult != Unknown; |
206 if (!atChange) | 213 if (!atChange) |
207 continue; | 214 continue; |
208 | 215 |
209 // Compute the range indices of consecutive shaped or .notdef glyphs. | 216 // Compute the range indices of consecutive shaped or .notdef glyphs. |
210 // Cluster information for RTL runs becomes reversed, e.g. character 0 | 217 // Cluster information for RTL runs becomes reversed, e.g. character 0 |
211 // has cluster index 5 in a run of 6 characters. | 218 // has cluster index 5 in a run of 6 characters. |
212 unsigned numCharacters = 0; | 219 unsigned numCharacters = 0; |
213 unsigned numGlyphsToInsert = 0; | 220 unsigned numGlyphsToInsert = 0; |
214 unsigned startIndex = 0; | 221 unsigned startIndex = 0; |
215 if (HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(harfBuzzBuffer))) { | 222 if (HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(buffer))) { |
216 startIndex = glyphInfo[lastChangePosition].cluster; | 223 startIndex = glyphInfo[lastChangePosition].cluster; |
217 if (glyphIndex == numGlyphs) { | 224 if (glyphIndex == numGlyphs) { |
218 numCharacters = currentQueueItem.m_startIndex + | 225 numCharacters = currentQueueItem.m_startIndex + |
219 currentQueueItem.m_numCharacters - | 226 currentQueueItem.m_numCharacters - |
220 glyphInfo[lastChangePosition].cluster; | 227 glyphInfo[lastChangePosition].cluster; |
221 numGlyphsToInsert = numGlyphs - lastChangePosition; | 228 numGlyphsToInsert = numGlyphs - lastChangePosition; |
222 } else { | 229 } else { |
223 numCharacters = glyphInfo[glyphIndex].cluster - | 230 numCharacters = glyphInfo[glyphIndex].cluster - |
224 glyphInfo[lastChangePosition].cluster; | 231 glyphInfo[lastChangePosition].cluster; |
225 numGlyphsToInsert = glyphIndex - lastChangePosition; | 232 numGlyphsToInsert = glyphIndex - lastChangePosition; |
(...skipping 27 matching lines...) Expand all Loading... | |
253 | 260 |
254 // If numCharacters is 0, that means we hit a NotDef before shaping the | 261 // 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 | 262 // whole grapheme. We do not append it here. For the next glyph we |
256 // encounter, atChange will be true, and the characters corresponding to | 263 // encounter, atChange will be true, and the characters corresponding to |
257 // the grapheme will be added to the TODO queue again, attempting to | 264 // the grapheme will be added to the TODO queue again, attempting to |
258 // shape the whole grapheme with the next font. | 265 // shape the whole grapheme with the next font. |
259 // When we're getting here with the last resort font, we have no other | 266 // When we're getting here with the last resort font, we have no other |
260 // choice than adding boxes to the ShapeResult. | 267 // choice than adding boxes to the ShapeResult. |
261 if ((currentClusterResult == NotDef && numCharacters) || isLastResort) { | 268 if ((currentClusterResult == NotDef && numCharacters) || isLastResort) { |
262 hb_direction_t direction = TextDirectionToHBDirection( | 269 hb_direction_t direction = TextDirectionToHBDirection( |
263 m_textDirection, font->getFontDescription().orientation(), | 270 textDirection, font->getFontDescription().orientation(), currentFont); |
264 currentFont); | |
265 // Here we need to specify glyph positions. | 271 // Here we need to specify glyph positions. |
266 ShapeResult::RunInfo* run = new ShapeResult::RunInfo( | 272 ShapeResult::RunInfo* run = new ShapeResult::RunInfo( |
267 currentFont, direction, ICUScriptToHBScript(currentRunScript), | 273 currentFont, direction, ICUScriptToHBScript(currentRunScript), |
268 startIndex, numGlyphsToInsert, numCharacters); | 274 startIndex, numGlyphsToInsert, numCharacters); |
269 shapeResult->insertRun(WTF::wrapUnique(run), lastChangePosition, | 275 shapeResult->insertRun(WTF::wrapUnique(run), lastChangePosition, |
270 numGlyphsToInsert, harfBuzzBuffer); | 276 numGlyphsToInsert, buffer); |
271 } | 277 } |
272 lastChangePosition = glyphIndex; | 278 lastChangePosition = glyphIndex; |
273 } | 279 } |
274 return true; | 280 return true; |
275 } | 281 } |
276 | 282 |
277 static inline const SimpleFontData* fontDataAdjustedForOrientation( | 283 static inline const SimpleFontData* fontDataAdjustedForOrientation( |
278 const SimpleFontData* originalFont, | 284 const SimpleFontData* originalFont, |
279 FontOrientation runOrientation, | 285 FontOrientation runOrientation, |
280 OrientationIterator::RenderOrientation renderOrientation) { | 286 OrientationIterator::RenderOrientation renderOrientation) { |
281 if (!isVerticalBaseline(runOrientation)) | 287 if (!isVerticalBaseline(runOrientation)) |
282 return originalFont; | 288 return originalFont; |
283 | 289 |
284 if (runOrientation == FontOrientation::VerticalRotated || | 290 if (runOrientation == FontOrientation::VerticalRotated || |
285 (runOrientation == FontOrientation::VerticalMixed && | 291 (runOrientation == FontOrientation::VerticalMixed && |
286 renderOrientation == OrientationIterator::OrientationRotateSideways)) | 292 renderOrientation == OrientationIterator::OrientationRotateSideways)) |
287 return originalFont->verticalRightOrientationFontData().get(); | 293 return originalFont->verticalRightOrientationFontData().get(); |
288 | 294 |
289 return originalFont; | 295 return originalFont; |
290 } | 296 } |
291 | 297 |
292 bool HarfBuzzShaper::collectFallbackHintChars( | 298 bool HarfBuzzShaper::collectFallbackHintChars( |
293 const Deque<HolesQueueItem>& holesQueue, | 299 const Deque<HolesQueueItem>* holesQueue, |
294 Vector<UChar32>& hint) const { | 300 Vector<UChar32>& hint) const { |
295 if (!holesQueue.size()) | 301 DCHECK(holesQueue); |
302 if (!holesQueue->size()) | |
296 return false; | 303 return false; |
297 | 304 |
298 hint.clear(); | 305 hint.clear(); |
299 | 306 |
300 size_t numCharsAdded = 0; | 307 size_t numCharsAdded = 0; |
301 for (auto it = holesQueue.begin(); it != holesQueue.end(); ++it) { | 308 for (auto it = holesQueue->begin(); it != holesQueue->end(); ++it) { |
302 if (it->m_action == HolesQueueNextFont) | 309 if (it->m_action == HolesQueueNextFont) |
303 break; | 310 break; |
304 | 311 |
305 UChar32 hintChar; | 312 UChar32 hintChar; |
306 RELEASE_ASSERT(it->m_startIndex + it->m_numCharacters <= | 313 RELEASE_ASSERT(it->m_startIndex + it->m_numCharacters <= m_textLength); |
307 m_normalizedBufferLength); | 314 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)) { | 315 while (iterator.consume(hintChar)) { |
311 hint.push_back(hintChar); | 316 hint.push_back(hintChar); |
312 numCharsAdded++; | 317 numCharsAdded++; |
313 iterator.advance(); | 318 iterator.advance(); |
314 } | 319 } |
315 } | 320 } |
316 return numCharsAdded > 0; | 321 return numCharsAdded > 0; |
317 } | 322 } |
318 | 323 |
319 namespace { | 324 namespace { |
320 using HolesQueueItem = HarfBuzzShaper::HolesQueueItem; | |
321 using HolesQueueItemAction = HarfBuzzShaper::HolesQueueItemAction; | |
322 | 325 |
323 void splitUntilNextCaseChange( | 326 void splitUntilNextCaseChange( |
324 const UChar* normalizedBuffer, | 327 const UChar* normalizedBuffer, |
325 Deque<HolesQueueItem>* queue, | 328 Deque<blink::HolesQueueItem>* queue, |
326 HolesQueueItem& currentQueueItem, | 329 blink::HolesQueueItem& currentQueueItem, |
327 SmallCapsIterator::SmallCapsBehavior& smallCapsBehavior) { | 330 SmallCapsIterator::SmallCapsBehavior& smallCapsBehavior) { |
328 unsigned numCharactersUntilCaseChange = 0; | 331 unsigned numCharactersUntilCaseChange = 0; |
329 SmallCapsIterator smallCapsIterator( | 332 SmallCapsIterator smallCapsIterator( |
330 normalizedBuffer + currentQueueItem.m_startIndex, | 333 normalizedBuffer + currentQueueItem.m_startIndex, |
331 currentQueueItem.m_numCharacters); | 334 currentQueueItem.m_numCharacters); |
332 smallCapsIterator.consume(&numCharactersUntilCaseChange, &smallCapsBehavior); | 335 smallCapsIterator.consume(&numCharactersUntilCaseChange, &smallCapsBehavior); |
333 if (numCharactersUntilCaseChange > 0 && | 336 if (numCharactersUntilCaseChange > 0 && |
334 numCharactersUntilCaseChange < currentQueueItem.m_numCharacters) { | 337 numCharactersUntilCaseChange < currentQueueItem.m_numCharacters) { |
335 queue->prepend(HolesQueueItem( | 338 queue->prepend(blink::HolesQueueItem( |
336 HolesQueueItemAction::HolesQueueRange, | 339 blink::HolesQueueItemAction::HolesQueueRange, |
337 currentQueueItem.m_startIndex + numCharactersUntilCaseChange, | 340 currentQueueItem.m_startIndex + numCharactersUntilCaseChange, |
338 currentQueueItem.m_numCharacters - numCharactersUntilCaseChange)); | 341 currentQueueItem.m_numCharacters - numCharactersUntilCaseChange)); |
339 currentQueueItem.m_numCharacters = numCharactersUntilCaseChange; | 342 currentQueueItem.m_numCharacters = numCharactersUntilCaseChange; |
340 } | 343 } |
341 } | 344 } |
342 | 345 |
343 hb_feature_t createFeature(hb_tag_t tag, uint32_t value = 0) { | 346 hb_feature_t createFeature(hb_tag_t tag, uint32_t value = 0) { |
344 return {tag, value, 0 /* start */, static_cast<unsigned>(-1) /* end */}; | 347 return {tag, value, 0 /* start */, static_cast<unsigned>(-1) /* end */}; |
345 } | 348 } |
346 | 349 |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
539 m_features->prepend(feature); | 542 m_features->prepend(feature); |
540 m_countFeatures++; | 543 m_countFeatures++; |
541 } | 544 } |
542 | 545 |
543 CapsFeatureSettingsScopedOverlay::~CapsFeatureSettingsScopedOverlay() { | 546 CapsFeatureSettingsScopedOverlay::~CapsFeatureSettingsScopedOverlay() { |
544 m_features->remove(0, m_countFeatures); | 547 m_features->remove(0, m_countFeatures); |
545 } | 548 } |
546 | 549 |
547 } // namespace | 550 } // namespace |
548 | 551 |
549 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeResult(const Font* font) const { | 552 void HarfBuzzShaper::shapeSegment(ShapeResult* result, |
550 RefPtr<ShapeResult> result = | 553 Deque<HolesQueueItem>* holesQueue, |
551 ShapeResult::create(font, m_normalizedBufferLength, m_textDirection); | 554 hb_buffer_t* buffer, |
552 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), | 555 const Font* font, |
553 hb_buffer_destroy); | 556 FeaturesVector* fontFeatures, |
554 FeaturesVector fontFeatures; | 557 RunSegmenter::RunSegmenterRange segment, |
555 setFontFeatures(font, &fontFeatures); | 558 unsigned start, |
559 unsigned end, | |
560 TextDirection textDirection) const { | |
561 DCHECK(result); | |
562 DCHECK(holesQueue); | |
563 DCHECK(buffer); | |
564 | |
565 holesQueue->append(HolesQueueItem(HolesQueueNextFont, 0, 0)); | |
566 holesQueue->append(HolesQueueItem(HolesQueueRange, segment.start, | |
567 segment.end - segment.start)); | |
568 | |
569 RefPtr<FontFallbackIterator> fallbackIterator = | |
570 font->createFontFallbackIterator(segment.fontFallbackPriority); | |
571 | |
572 RefPtr<FontDataForRangeSet> currentFontDataForRangeSet; | |
573 | |
556 const FontDescription& fontDescription = font->getFontDescription(); | 574 const FontDescription& fontDescription = font->getFontDescription(); |
575 bool fontCycleQueued = false; | |
576 bool needsCapsHandling = | |
577 fontDescription.variantCaps() != FontDescription::CapsNormal; | |
578 FontOrientation orientation = fontDescription.orientation(); | |
557 const hb_language_t language = | 579 const hb_language_t language = |
558 fontDescription.localeOrDefault().harfbuzzLanguage(); | 580 fontDescription.localeOrDefault().harfbuzzLanguage(); |
559 | 581 |
560 bool needsCapsHandling = | |
561 fontDescription.variantCaps() != FontDescription::CapsNormal; | |
562 OpenTypeCapsSupport capsSupport; | 582 OpenTypeCapsSupport capsSupport; |
583 Vector<UChar32> fallbackCharsHint; | |
584 while (holesQueue->size()) { | |
585 HolesQueueItem currentQueueItem = holesQueue->takeFirst(); | |
586 DCHECK(currentQueueItem.m_numCharacters); | |
587 | |
588 if (currentQueueItem.m_action == HolesQueueNextFont) { | |
589 // For now, we're building a character list with which we probe | |
590 // for needed fonts depending on the declared unicode-range of a | |
591 // segmented CSS font. Alternatively, we can build a fake font | |
592 // for the shaper and check whether any glyphs were found, or | |
593 // define a new API on the shaper which will give us coverage | |
594 // information? | |
595 if (!collectFallbackHintChars(holesQueue, fallbackCharsHint)) { | |
596 // Give up shaping since we cannot retrieve a font fallback | |
597 // font without a hintlist. | |
598 holesQueue->clear(); | |
599 break; | |
600 } | |
601 | |
602 currentFontDataForRangeSet = fallbackIterator->next(fallbackCharsHint); | |
603 if (!currentFontDataForRangeSet->fontData()) { | |
604 DCHECK(!holesQueue->size()); | |
605 break; | |
606 } | |
607 fontCycleQueued = false; | |
608 continue; | |
609 } | |
610 | |
611 const SimpleFontData* fontData = currentFontDataForRangeSet->fontData(); | |
612 SmallCapsIterator::SmallCapsBehavior smallCapsBehavior = | |
613 SmallCapsIterator::SmallCapsSameCase; | |
614 if (needsCapsHandling) { | |
615 capsSupport = OpenTypeCapsSupport(fontData->platformData().harfBuzzFace(), | |
616 fontDescription.variantCaps(), | |
617 ICUScriptToHBScript(segment.script)); | |
618 if (capsSupport.needsRunCaseSplitting()) { | |
619 splitUntilNextCaseChange(m_text, holesQueue, currentQueueItem, | |
620 smallCapsBehavior); | |
621 } | |
622 } | |
623 | |
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 CaseMappingHarfBuzzBufferFiller( | |
642 caseMapIntend, fontDescription.localeOrDefault(), buffer, | |
643 m_text + start, m_textLength, currentQueueItem.m_startIndex, | |
644 currentQueueItem.m_numCharacters); | |
645 | |
646 CapsFeatureSettingsScopedOverlay capsOverlay( | |
647 fontFeatures, capsSupport.fontFeatureToUse(smallCapsBehavior)); | |
648 hb_direction_t direction = TextDirectionToHBDirection( | |
649 textDirection, orientation, directionAndSmallCapsAdjustedFont); | |
650 | |
651 if (!shapeRange(buffer, fontFeatures->isEmpty() ? 0 : fontFeatures->data(), | |
652 fontFeatures->size(), directionAndSmallCapsAdjustedFont, | |
653 currentFontDataForRangeSet->ranges(), segment.script, | |
654 direction, language)) | |
655 DLOG(ERROR) << "Shaping range failed."; | |
656 | |
657 if (!extractShapeResults(buffer, result, fontCycleQueued, holesQueue, | |
658 currentQueueItem, font, textDirection, | |
659 directionAndSmallCapsAdjustedFont, segment.script, | |
660 !fallbackIterator->hasNext())) | |
661 DLOG(ERROR) << "Shape result extraction failed."; | |
662 | |
663 hb_buffer_reset(buffer); | |
664 } | |
665 } | |
666 | |
667 PassRefPtr<ShapeResult> HarfBuzzShaper::shape(const Font* font, | |
668 TextDirection textDirection, | |
669 unsigned start, | |
670 unsigned end) const { | |
671 DCHECK(end >= start); | |
672 DCHECK(end <= m_textLength); | |
673 | |
674 unsigned length = end - start; | |
675 RefPtr<ShapeResult> result = ShapeResult::create(font, length, textDirection); | |
676 HarfBuzzScopedPtr<hb_buffer_t> buffer(hb_buffer_create(), hb_buffer_destroy); | |
677 | |
678 FeaturesVector fontFeatures; | |
679 setFontFeatures(font, &fontFeatures); | |
563 FontOrientation orientation = font->getFontDescription().orientation(); | 680 FontOrientation orientation = font->getFontDescription().orientation(); |
564 | 681 |
565 RunSegmenter::RunSegmenterRange segmentRange = { | 682 Deque<HolesQueueItem> holesQueue; |
566 0, 0, USCRIPT_INVALID_CODE, OrientationIterator::OrientationInvalid, | 683 RunSegmenter::RunSegmenterRange segmentRange = RunSegmenter::nullRange(); |
567 FontFallbackPriority::Invalid}; | 684 RunSegmenter runSegmenter(m_text + start, length, orientation); |
drott
2017/02/07 23:12:41
Offsetting the RunSegmenter to a start at an offse
| |
568 RunSegmenter runSegmenter(m_normalizedBuffer, m_normalizedBufferLength, | |
569 orientation); | |
570 | |
571 Vector<UChar32> fallbackCharsHint; | |
572 | 685 |
573 // TODO: Check whether this treatAsZerowidthspace from the previous script | 686 // TODO: Check whether this treatAsZerowidthspace from the previous script |
574 // segmentation plays a role here, does the new scriptRuniterator handle that | 687 // segmentation plays a role here, does the new scriptRuniterator handle |
575 // correctly? | 688 // that correctly? |
576 Deque<HolesQueueItem> holesQueue; | |
577 while (runSegmenter.consume(&segmentRange)) { | 689 while (runSegmenter.consume(&segmentRange)) { |
578 RefPtr<FontFallbackIterator> fallbackIterator = | 690 shapeSegment(result.get(), &holesQueue, buffer.get(), font, &fontFeatures, |
579 font->createFontFallbackIterator(segmentRange.fontFallbackPriority); | 691 segmentRange, start, end, textDirection); |
692 } | |
580 | 693 |
581 holesQueue.append(HolesQueueItem(HolesQueueNextFont, 0, 0)); | |
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 } | |
673 } | |
674 return result.release(); | 694 return result.release(); |
675 } | 695 } |
676 | 696 |
697 PassRefPtr<ShapeResult> HarfBuzzShaper::shape( | |
698 const Font* font, | |
699 TextDirection textDirection) const { | |
700 return shape(font, textDirection, 0, m_textLength); | |
701 } | |
677 } // namespace blink | 702 } // namespace blink |
OLD | NEW |