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, 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 |