diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-03 14:13:37 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-03 14:13:37 +0300 |
commit | bd1504bbefc8954557044691bddd463c0d0e1911 (patch) | |
tree | 7989d18610acdb2e593ec8cc523cbaa871a69fcb /src | |
parent | 1828df7b3260907a18096c438814f90e048da908 (diff) |
Text: Use FriBidi for visual order
Todo: This seems a little late in the game. The reordering should be done for each paragraph of text.
Diffstat (limited to 'src')
-rw-r--r-- | src/ui/text.c | 65 | ||||
-rw-r--r-- | src/ui/text.h | 1 |
2 files changed, 61 insertions, 5 deletions
diff --git a/src/ui/text.c b/src/ui/text.c index 786e11ff..175b25a1 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -142,11 +142,13 @@ struct Impl_Font { | |||
142 | 142 | ||
143 | static iFont *font_Text_(enum iFontId id); | 143 | static iFont *font_Text_(enum iFontId id); |
144 | 144 | ||
145 | #if 0 | ||
145 | static hb_position_t hbGlyphHKernForNunito_(hb_font_t *font, void *fontData, | 146 | static hb_position_t hbGlyphHKernForNunito_(hb_font_t *font, void *fontData, |
146 | hb_codepoint_t firstGlyph, hb_codepoint_t secondGlyph, | 147 | hb_codepoint_t firstGlyph, hb_codepoint_t secondGlyph, |
147 | void *userData) { | 148 | void *userData) { |
148 | return 100; | 149 | return 100; |
149 | } | 150 | } |
151 | #endif | ||
150 | 152 | ||
151 | static void init_Font(iFont *d, const iBlock *data, int height, float scale, | 153 | static void init_Font(iFont *d, const iBlock *data, int height, float scale, |
152 | enum iFontSize sizeId, iBool isMonospaced) { | 154 | enum iFontSize sizeId, iBool isMonospaced) { |
@@ -161,6 +163,9 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale, | |||
161 | data == &fontNunitoExtraLight_Embedded) { | 163 | data == &fontNunitoExtraLight_Embedded) { |
162 | d->family = nunito_TextFont; | 164 | d->family = nunito_TextFont; |
163 | } | 165 | } |
166 | else if (data == &fontNotoSansArabicUIRegular_Embedded) { | ||
167 | d->family = notoSansArabic_TextFont; | ||
168 | } | ||
164 | d->isMonospaced = isMonospaced; | 169 | d->isMonospaced = isMonospaced; |
165 | d->height = height; | 170 | d->height = height; |
166 | iZap(d->font); | 171 | iZap(d->font); |
@@ -851,7 +856,12 @@ static void prepare_AttributedText_(iAttributedText *d) { | |||
851 | run.text.end = currentPos; | 856 | run.text.end = currentPos; |
852 | break; | 857 | break; |
853 | } | 858 | } |
854 | const iGlyph *glyph = glyph_Font_(d->font, ch); | 859 | iFont *currentFont = d->font; |
860 | if (run.font->family == notoSansArabic_TextFont && ch == 0x20) { | ||
861 | currentFont = run.font; /* remain as Arabic for whitespace */ | ||
862 | /* TODO: FriBidi should be applied before this loop, but how to map the positions? */ | ||
863 | } | ||
864 | const iGlyph *glyph = glyph_Font_(currentFont, ch); | ||
855 | if (index_Glyph_(glyph) && glyph->font != run.font) { | 865 | if (index_Glyph_(glyph) && glyph->font != run.font) { |
856 | /* A different font is being used for this character. */ | 866 | /* A different font is being used for this character. */ |
857 | finishRun_AttributedText_(d, &run, currentPos); | 867 | finishRun_AttributedText_(d, &run, currentPos); |
@@ -1160,18 +1170,61 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1160 | hb_buffer_clear_contents(hbBuf); | 1170 | hb_buffer_clear_contents(hbBuf); |
1161 | /* Cluster values are used to determine offset inside the UTF-8 source string. */ | 1171 | /* Cluster values are used to determine offset inside the UTF-8 source string. */ |
1162 | //hb_buffer_set_cluster_level(hbBuf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); | 1172 | //hb_buffer_set_cluster_level(hbBuf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); |
1173 | #if 0 && defined (LAGRANGE_ENABLE_FRIBIDI) | ||
1174 | /* Reorder to visual order. */ { | ||
1175 | iArray u32; | ||
1176 | iArray logToRun; | ||
1177 | init_Array(&u32, sizeof(uint32_t)); | ||
1178 | init_Array(&logToRun, sizeof(FriBidiStrIndex)); | ||
1179 | for (const char *pos = runText.start; pos < runText.end; ) { | ||
1180 | iChar ucp = 0; | ||
1181 | const int len = decodeBytes_MultibyteChar(pos, runText.end, &ucp); | ||
1182 | if (len > 0) { | ||
1183 | pushBack_Array(&u32, &ucp); | ||
1184 | pushBack_Array(&logToRun, &(int){ pos - runText.start }); | ||
1185 | pos += len; | ||
1186 | } | ||
1187 | else break; | ||
1188 | } | ||
1189 | iArray vis32; | ||
1190 | init_Array(&vis32, sizeof(uint32_t)); | ||
1191 | resize_Array(&vis32, size_Array(&u32)); | ||
1192 | iArray visToLog; | ||
1193 | init_Array(&visToLog, sizeof(FriBidiStrIndex)); | ||
1194 | resize_Array(&visToLog, size_Array(&u32)); | ||
1195 | FriBidiParType baseDir = (FriBidiParType) FRIBIDI_TYPE_ON; | ||
1196 | fribidi_log2vis(constData_Array(&u32), | ||
1197 | size_Array(&u32), | ||
1198 | &baseDir, | ||
1199 | data_Array(&vis32), | ||
1200 | NULL, | ||
1201 | data_Array(&visToLog), | ||
1202 | NULL); | ||
1203 | const FriBidiStrIndex *visToLogIndex = constData_Array(&visToLog); | ||
1204 | const FriBidiStrIndex *logToRunIndex = constData_Array(&logToRun); | ||
1205 | iConstForEach(Array, v, &vis32) { | ||
1206 | hb_buffer_add(hbBuf, | ||
1207 | *(const hb_codepoint_t *) v.value, | ||
1208 | logToRunIndex[visToLogIndex[index_ArrayConstIterator(&v)]]); | ||
1209 | } | ||
1210 | deinit_Array(&visToLog); | ||
1211 | deinit_Array(&vis32); | ||
1212 | deinit_Array(&logToRun); | ||
1213 | deinit_Array(&u32); | ||
1214 | } | ||
1215 | #else /* !defined (LAGRANGE_ENABLE_FRIBIDI) */ | ||
1163 | for (const char *pos = runText.start; pos < runText.end; ) { | 1216 | for (const char *pos = runText.start; pos < runText.end; ) { |
1164 | iChar ucp = 0; | 1217 | iChar ucp = 0; |
1165 | const int len = decodeBytes_MultibyteChar(pos, runText.end, &ucp); | 1218 | const int len = decodeBytes_MultibyteChar(pos, runText.end, &ucp); |
1166 | // if (ucp == 0xad) ucp = '-'; | ||
1167 | if (len > 0) { | 1219 | if (len > 0) { |
1168 | hb_buffer_add(hbBuf, ucp, pos - runText.start); | 1220 | hb_buffer_add(hbBuf, ucp, pos - runText.start); |
1169 | pos += len; | 1221 | pos += len; |
1170 | } | 1222 | } |
1171 | else break; | 1223 | else break; |
1172 | } | 1224 | } |
1225 | #endif | ||
1173 | hb_buffer_set_content_type(hbBuf, HB_BUFFER_CONTENT_TYPE_UNICODE); | 1226 | hb_buffer_set_content_type(hbBuf, HB_BUFFER_CONTENT_TYPE_UNICODE); |
1174 | hb_buffer_set_direction(hbBuf, HB_DIRECTION_LTR); /* TODO: FriBidi? */ | 1227 | hb_buffer_set_direction(hbBuf, HB_DIRECTION_LTR); |
1175 | /* hb_buffer_set_script(hbBuf, HB_SCRIPT_LATIN); */ /* will be autodetected */ | 1228 | /* hb_buffer_set_script(hbBuf, HB_SCRIPT_LATIN); */ /* will be autodetected */ |
1176 | //hb_buffer_set_language(hbBuf, hb_language_from_string("en", -1)); /* TODO: language from document/UI, if known */ | 1229 | //hb_buffer_set_language(hbBuf, hb_language_from_string("en", -1)); /* TODO: language from document/UI, if known */ |
1177 | hb_shape(run->font->hbFont, hbBuf, NULL, 0); /* TODO: Specify features, too? */ | 1230 | hb_shape(run->font->hbFont, hbBuf, NULL, 0); /* TODO: Specify features, too? */ |
@@ -1264,6 +1317,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1264 | const hb_glyph_info_t *info = &glyphInfo[i]; | 1317 | const hb_glyph_info_t *info = &glyphInfo[i]; |
1265 | const hb_codepoint_t glyphId = info->codepoint; | 1318 | const hb_codepoint_t glyphId = info->codepoint; |
1266 | const char *textPos = runText.start + info->cluster; | 1319 | const char *textPos = runText.start + info->cluster; |
1320 | iAssert(textPos >= runText.start); | ||
1321 | iAssert(textPos < runText.end); | ||
1267 | const float xOffset = run->font->xScale * glyphPos[i].x_offset; | 1322 | const float xOffset = run->font->xScale * glyphPos[i].x_offset; |
1268 | const float yOffset = run->font->yScale * glyphPos[i].y_offset; | 1323 | const float yOffset = run->font->yScale * glyphPos[i].y_offset; |
1269 | const float xAdvance = run->font->xScale * glyphPos[i].x_advance; | 1324 | const float xAdvance = run->font->xScale * glyphPos[i].x_advance; |
@@ -1405,8 +1460,8 @@ iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) | |||
1405 | iWrapText wrap = { .mode = word_WrapTextMode, | 1460 | iWrapText wrap = { .mode = word_WrapTextMode, |
1406 | .text = text, .maxWidth = width, | 1461 | .text = text, .maxWidth = width, |
1407 | .wrapFunc = cbAdvanceOneLine_, .context = endPos }; | 1462 | .wrapFunc = cbAdvanceOneLine_, .context = endPos }; |
1408 | //const int x = advance_WrapText(&wrap, fontId).x; | 1463 | /* The return value is expected to be the horizontal/vertical bounds, while WrapText |
1409 | //return init_I2(x, lineHeight_Text(fontId)); | 1464 | returns the actual advanced cursor position. */ |
1410 | return addY_I2(advance_WrapText(&wrap, fontId), lineHeight_Text(fontId)); | 1465 | return addY_I2(advance_WrapText(&wrap, fontId), lineHeight_Text(fontId)); |
1411 | 1466 | ||
1412 | #if 0 | 1467 | #if 0 |
diff --git a/src/ui/text.h b/src/ui/text.h index 26965f52..e5b57bf4 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -125,6 +125,7 @@ enum iTextFont { | |||
125 | tinos_TextFont, | 125 | tinos_TextFont, |
126 | sourceSans3_TextFont, | 126 | sourceSans3_TextFont, |
127 | iosevka_TextFont, | 127 | iosevka_TextFont, |
128 | notoSansArabic_TextFont, | ||
128 | }; | 129 | }; |
129 | 130 | ||
130 | extern int gap_Text; /* affected by content font size */ | 131 | extern int gap_Text; /* affected by content font size */ |