Chromium Code Reviews| 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 |