@@ -19,12 +19,19 @@ namespace textlayout {
1919namespace {
2020
2121using ICUUText = std::unique_ptr<UText, SkFunctionWrapper<decltype (utext_close), utext_close>>;
22+ using ICUBiDi = std::unique_ptr<UBiDi, SkFunctionWrapper<decltype (ubidi_close), ubidi_close>>;
2223
2324SkScalar littleRound (SkScalar a) {
2425 // This rounding is done to match Flutter tests. Must be removed..
2526 return SkScalarRoundToScalar (a * 100.0 )/100.0 ;
2627}
2728
29+ /* * Replaces invalid utf-8 sequences with REPLACEMENT CHARACTER U+FFFD. */
30+ static inline SkUnichar utf8_next (const char ** ptr, const char * end) {
31+ SkUnichar val = SkUTF::NextUTF8 (ptr, end);
32+ return val < 0 ? 0xFFFD : val;
33+ }
34+
2835}
2936
3037TextRange operator *(const TextRange& a, const TextRange& b) {
@@ -674,11 +681,14 @@ std::vector<TextBox> ParagraphImpl::getRectsForRange(unsigned start,
674681 }
675682 }
676683
684+ auto runInLineWidth = line.measureTextInsideOneRun (textRange, run, runOffset, 0 , true , false ).clip .width ();
677685 runOffset += *width;
678686
679687 // Found a run that intersects with the text
680688 auto context = line.measureTextInsideOneRun (intersect, run, runOffset, 0 , true , true );
681- *width += context.clip .width ();
689+
690+ // *width += context.clip.width();
691+ *width = runInLineWidth;
682692
683693 SkRect clip = context.clip ;
684694 SkRect trailingSpaces = SkRect::MakeEmpty ();
@@ -1132,5 +1142,78 @@ void ParagraphImpl::updateBackgroundPaint(size_t from, size_t to, SkPaint paint)
11321142 }
11331143}
11341144
1145+ bool ParagraphImpl::calculateBidiRegions (SkTArray<BidiRegion>* regions) {
1146+
1147+ regions->reset ();
1148+
1149+ // ubidi only accepts utf16 (though internally it basically works on utf32 chars).
1150+ // We want an ubidi_setPara(UBiDi*, UText*, UBiDiLevel, UBiDiLevel*, UErrorCode*);
1151+ size_t utf8Bytes = fText .size ();
1152+ const char * utf8 = fText .c_str ();
1153+ uint8_t bidiLevel = fParagraphStyle .getTextDirection () == TextDirection::kLtr
1154+ ? UBIDI_LTR
1155+ : UBIDI_RTL;
1156+ if (!SkTFitsIn<int32_t >(utf8Bytes)) {
1157+ SkDEBUGF (" Bidi error: text too long" );
1158+ return false ;
1159+ }
1160+
1161+ // Getting the length like this seems to always set U_BUFFER_OVERFLOW_ERROR
1162+ UErrorCode status = U_ZERO_ERROR;
1163+ int32_t utf16Units;
1164+ u_strFromUTF8 (nullptr , 0 , &utf16Units, utf8, utf8Bytes, &status);
1165+ status = U_ZERO_ERROR;
1166+ std::unique_ptr<UChar[]> utf16 (new UChar[utf16Units]);
1167+ u_strFromUTF8 (utf16.get (), utf16Units, nullptr , utf8, utf8Bytes, &status);
1168+ if (U_FAILURE (status)) {
1169+ SkDEBUGF (" Invalid utf8 input: %s" , u_errorName (status));
1170+ return false ;
1171+ }
1172+
1173+ ICUBiDi bidi (ubidi_openSized (utf16Units, 0 , &status));
1174+ if (U_FAILURE (status)) {
1175+ SkDEBUGF (" Bidi error: %s" , u_errorName (status));
1176+ return false ;
1177+ }
1178+ SkASSERT (bidi);
1179+
1180+ // The required lifetime of utf16 isn't well documented.
1181+ // It appears it isn't used after ubidi_setPara except through ubidi_getText.
1182+ ubidi_setPara (bidi.get (), utf16.get (), utf16Units, bidiLevel, nullptr , &status);
1183+ if (U_FAILURE (status)) {
1184+ SkDEBUGF (" Bidi error: %s" , u_errorName (status));
1185+ return false ;
1186+ }
1187+
1188+ SkTArray<BidiRegion> bidiRegions;
1189+ const char * start8 = utf8;
1190+ const char * end8 = utf8 + utf8Bytes;
1191+ TextRange textRange (0 , 0 );
1192+ UBiDiLevel currentLevel = 0 ;
1193+
1194+ int32_t pos16 = 0 ;
1195+ int32_t end16 = ubidi_getLength (bidi.get ());
1196+ while (pos16 < end16) {
1197+ auto level = ubidi_getLevelAt (bidi.get (), pos16);
1198+ if (pos16 == 0 ) {
1199+ currentLevel = level;
1200+ } else if (level != currentLevel) {
1201+ textRange.end = start8 - utf8;
1202+ regions->emplace_back (textRange.start , textRange.end , currentLevel);
1203+ currentLevel = level;
1204+ textRange = TextRange (textRange.end , textRange.end );
1205+ }
1206+ SkUnichar u = utf8_next (&start8, end8);
1207+ pos16 += SkUTF::ToUTF16 (u);
1208+ }
1209+
1210+ textRange.end = start8 - utf8;
1211+ if (!textRange.empty ()) {
1212+ regions->emplace_back (textRange.start , textRange.end , currentLevel);
1213+ }
1214+
1215+ return true ;
1216+ }
1217+
11351218} // namespace textlayout
11361219} // namespace skia
0 commit comments