diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-04 17:59:05 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-04 17:59:05 +0300 |
commit | 9ffc5aeb1349bb954582278e3e6f4d9a824f4284 (patch) | |
tree | 23359ac4633a4dbbf0a82ed50144bd5936745992 /src/ui/text.c | |
parent | 68b67c1de0754a763a138377b2b1250313a7eaf0 (diff) |
Text: Apply FriBidi (again); fixed wrapping bugs
Diffstat (limited to 'src/ui/text.c')
-rw-r--r-- | src/ui/text.c | 157 |
1 files changed, 88 insertions, 69 deletions
diff --git a/src/ui/text.c b/src/ui/text.c index aa2125b1..a21845b6 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -791,6 +791,7 @@ struct Impl_AttributedText { | |||
791 | iArray runs; | 791 | iArray runs; |
792 | iArray visual; /* UTF-32 text in visual order (LTR) */ | 792 | iArray visual; /* UTF-32 text in visual order (LTR) */ |
793 | iArray visualToSourceOffset; /* map visual character to an UTF-8 offset in the source text */ | 793 | iArray visualToSourceOffset; /* map visual character to an UTF-8 offset in the source text */ |
794 | char * bidiLevels; | ||
794 | }; | 795 | }; |
795 | 796 | ||
796 | iDefineTypeConstructionArgs(AttributedText, (iRangecc text, size_t maxLen, iFont *font, iColor fgColor), | 797 | iDefineTypeConstructionArgs(AttributedText, (iRangecc text, size_t maxLen, iFont *font, iColor fgColor), |
@@ -849,27 +850,80 @@ static void prepare_AttributedText_(iAttributedText *d) { | |||
849 | pushBack_Array(&d->visualToSourceOffset, &(int){ ch - d->source.start }); | 850 | pushBack_Array(&d->visualToSourceOffset, &(int){ ch - d->source.start }); |
850 | ch += len; | 851 | ch += len; |
851 | } | 852 | } |
852 | /* Location of the terminating null. */ | ||
853 | pushBack_Array(&d->visualToSourceOffset, &(int){ d->source.end - d->source.start }); | ||
854 | #if defined (LAGRANGE_ENABLE_FRIBIDI) | 853 | #if defined (LAGRANGE_ENABLE_FRIBIDI) |
855 | /* TODO: Use FriBidi to reorder the codepoints. */ | 854 | /* Use FriBidi to reorder the codepoints. */ |
856 | 855 | // iArray u32; | |
856 | // iArray logToRun; | ||
857 | // init_Array(&u32, sizeof(uint32_t)); | ||
858 | // init_Array(&logToRun, sizeof(FriBidiStrIndex)); | ||
859 | // for (const char *pos = runText.start; pos < runText.end; ) { | ||
860 | // iChar ucp = 0; | ||
861 | // const int len = decodeBytes_MultibyteChar(pos, runText.end, &ucp); | ||
862 | // if (len > 0) { | ||
863 | // pushBack_Array(&u32, &ucp); | ||
864 | // pushBack_Array(&logToRun, &(int){ pos - runText.start }); | ||
865 | // pos += len; | ||
866 | // } | ||
867 | // else break; | ||
868 | // } | ||
869 | const size_t len = size_Array(&d->visual); | ||
870 | iArray ordered; | ||
871 | iArray visToLog; | ||
872 | init_Array(&ordered, sizeof(uint32_t)); | ||
873 | init_Array(&visToLog, sizeof(FriBidiStrIndex)); | ||
874 | resize_Array(&ordered, len); | ||
875 | resize_Array(&visToLog, len); | ||
876 | d->bidiLevels = malloc(len); | ||
877 | FriBidiParType baseDir = (FriBidiParType) FRIBIDI_TYPE_ON; | ||
878 | fribidi_log2vis(constData_Array(&d->visual), | ||
879 | len, | ||
880 | &baseDir, | ||
881 | data_Array(&ordered), | ||
882 | NULL, | ||
883 | data_Array(&visToLog), | ||
884 | (FriBidiLevel *) d->bidiLevels); | ||
885 | // if (FRIBIDI_IS_RTL(baseDir)) { | ||
886 | // isRunRTL = iTrue; | ||
887 | // } | ||
888 | /* Replace with the visually ordered codepoints. */ | ||
889 | setCopy_Array(&d->visual, &ordered); | ||
890 | deinit_Array(&ordered); | ||
891 | /* Remap the source positions for visual order. */ | ||
892 | const int *mapToSource = constData_Array(&d->visualToSourceOffset); | ||
893 | const FriBidiStrIndex *visToLogIndex = constData_Array(&visToLog); | ||
894 | iArray orderedMapToSource; | ||
895 | init_Array(&orderedMapToSource, sizeof(int)); | ||
896 | resize_Array(&orderedMapToSource, len); | ||
897 | for (size_t i = 0; i < len; i++) { | ||
898 | *(int *) at_Array(&orderedMapToSource, i) = mapToSource[visToLogIndex[i]]; | ||
899 | } | ||
900 | setCopy_Array(&d->visualToSourceOffset, &orderedMapToSource); | ||
901 | deinit_Array(&orderedMapToSource); | ||
902 | deinit_Array(&visToLog); | ||
903 | //const FriBidiStrIndex *logToRunIndex = constData_Array(&logToRun); | ||
904 | // iConstForEach(Array, v, &vis32) { | ||
905 | // hb_buffer_add(hbBuf, | ||
906 | // *(const hb_codepoint_t *) v.value, | ||
907 | // logToRunIndex[visToLogIndex[index_ArrayConstIterator(&v)]]); | ||
908 | // } | ||
909 | // deinit_Array(&visToLog); | ||
910 | // deinit_Array(&vis32); | ||
911 | // deinit_Array(&logToRun); | ||
912 | // deinit_Array(&u32); | ||
857 | #endif | 913 | #endif |
858 | } | 914 | } |
859 | // iRangecc srcText = d->source; | 915 | /* The mapping needs to include the terminating NULL position. */ { |
916 | pushBack_Array(&d->visualToSourceOffset, &(int){ d->source.end - d->source.start }); | ||
917 | } | ||
860 | iRangei visText = { 0, size_Array(&d->visual) }; | 918 | iRangei visText = { 0, size_Array(&d->visual) }; |
861 | size_t avail = d->maxLen; | 919 | size_t avail = d->maxLen; |
862 | //const char *srcPos = srcText.start; | ||
863 | iAttributedRun run = { .visual = visText, | 920 | iAttributedRun run = { .visual = visText, |
864 | // .source = d->source, | ||
865 | .font = d->font, | 921 | .font = d->font, |
866 | .fgColor = d->fgColor }; | 922 | .fgColor = d->fgColor }; |
867 | //while (chPos < visText.end) { | 923 | const int * mapToSource = constData_Array(&d->visualToSourceOffset); |
868 | const int *mapToSource = constData_Array(&d->visualToSourceOffset); | 924 | const iChar *visualText = constData_Array(&d->visual); |
869 | const iChar *visualText = constData_Array(&d->visual); | ||
870 | for (int pos = 0; pos < visText.end; pos++) { | 925 | for (int pos = 0; pos < visText.end; pos++) { |
871 | const iChar ch = visualText[pos]; | 926 | const iChar ch = visualText[pos]; |
872 | //const char *currentPos = chPos; | ||
873 | if (ch == 0x1b) { /* ANSI escape. */ | 927 | if (ch == 0x1b) { /* ANSI escape. */ |
874 | pos++; | 928 | pos++; |
875 | const char *srcPos = d->source.start + mapToSource[pos]; | 929 | const char *srcPos = d->source.start + mapToSource[pos]; |
@@ -944,6 +998,15 @@ static void prepare_AttributedText_(iAttributedText *d) { | |||
944 | if (!isEmpty_Range(&run.visual)) { | 998 | if (!isEmpty_Range(&run.visual)) { |
945 | pushBack_Array(&d->runs, &run); | 999 | pushBack_Array(&d->runs, &run); |
946 | } | 1000 | } |
1001 | #if 0 | ||
1002 | printf("[AttributedText] %zu runs:\n", size_Array(&d->runs)); | ||
1003 | iConstForEach(Array, i, &d->runs) { | ||
1004 | const iAttributedRun *run = i.value; | ||
1005 | printf(" %zu %d...%d {%s}\n", index_ArrayConstIterator(&i), | ||
1006 | run->visual.start, run->visual.end, | ||
1007 | cstr_Rangecc(sourceRange_AttributedText_(d, run->visual))); | ||
1008 | } | ||
1009 | #endif | ||
947 | } | 1010 | } |
948 | 1011 | ||
949 | void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, iColor fgColor) { | 1012 | void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, iColor fgColor) { |
@@ -954,10 +1017,12 @@ void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont | |||
954 | init_Array(&d->runs, sizeof(iAttributedRun)); | 1017 | init_Array(&d->runs, sizeof(iAttributedRun)); |
955 | init_Array(&d->visual, sizeof(iChar)); | 1018 | init_Array(&d->visual, sizeof(iChar)); |
956 | init_Array(&d->visualToSourceOffset, sizeof(int)); | 1019 | init_Array(&d->visualToSourceOffset, sizeof(int)); |
1020 | d->bidiLevels = NULL; | ||
957 | prepare_AttributedText_(d); | 1021 | prepare_AttributedText_(d); |
958 | } | 1022 | } |
959 | 1023 | ||
960 | void deinit_AttributedText(iAttributedText *d) { | 1024 | void deinit_AttributedText(iAttributedText *d) { |
1025 | free(d->bidiLevels); | ||
961 | deinit_Array(&d->visual); | 1026 | deinit_Array(&d->visual); |
962 | deinit_Array(&d->visualToSourceOffset); | 1027 | deinit_Array(&d->visualToSourceOffset); |
963 | deinit_Array(&d->runs); | 1028 | deinit_Array(&d->runs); |
@@ -1267,56 +1332,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1267 | iBool isRunRTL = iFalse; | 1332 | iBool isRunRTL = iFalse; |
1268 | /* Cluster values are used to determine offset inside the UTF-8 source string. */ | 1333 | /* Cluster values are used to determine offset inside the UTF-8 source string. */ |
1269 | //hb_buffer_set_cluster_level(hbBuf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); | 1334 | //hb_buffer_set_cluster_level(hbBuf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); |
1270 | #if 0 && defined (LAGRANGE_ENABLE_FRIBIDI) | ||
1271 | /* Reorder to visual order. */ { | ||
1272 | iArray u32; | ||
1273 | iArray logToRun; | ||
1274 | init_Array(&u32, sizeof(uint32_t)); | ||
1275 | init_Array(&logToRun, sizeof(FriBidiStrIndex)); | ||
1276 | for (const char *pos = runText.start; pos < runText.end; ) { | ||
1277 | iChar ucp = 0; | ||
1278 | const int len = decodeBytes_MultibyteChar(pos, runText.end, &ucp); | ||
1279 | if (len > 0) { | ||
1280 | pushBack_Array(&u32, &ucp); | ||
1281 | pushBack_Array(&logToRun, &(int){ pos - runText.start }); | ||
1282 | pos += len; | ||
1283 | } | ||
1284 | else break; | ||
1285 | } | ||
1286 | iArray vis32; | ||
1287 | init_Array(&vis32, sizeof(uint32_t)); | ||
1288 | resize_Array(&vis32, size_Array(&u32)); | ||
1289 | iArray visToLog; | ||
1290 | init_Array(&visToLog, sizeof(FriBidiStrIndex)); | ||
1291 | resize_Array(&visToLog, size_Array(&u32)); | ||
1292 | FriBidiParType baseDir = (FriBidiParType) FRIBIDI_TYPE_ON; | ||
1293 | fribidi_log2vis(constData_Array(&u32), | ||
1294 | size_Array(&u32), | ||
1295 | &baseDir, | ||
1296 | data_Array(&vis32), | ||
1297 | NULL, | ||
1298 | data_Array(&visToLog), | ||
1299 | NULL); | ||
1300 | if (FRIBIDI_IS_RTL(baseDir)) { | ||
1301 | isRunRTL = iTrue; | ||
1302 | } | ||
1303 | const FriBidiStrIndex *visToLogIndex = constData_Array(&visToLog); | ||
1304 | const FriBidiStrIndex *logToRunIndex = constData_Array(&logToRun); | ||
1305 | iConstForEach(Array, v, &vis32) { | ||
1306 | hb_buffer_add(hbBuf, | ||
1307 | *(const hb_codepoint_t *) v.value, | ||
1308 | logToRunIndex[visToLogIndex[index_ArrayConstIterator(&v)]]); | ||
1309 | } | ||
1310 | deinit_Array(&visToLog); | ||
1311 | deinit_Array(&vis32); | ||
1312 | deinit_Array(&logToRun); | ||
1313 | deinit_Array(&u32); | ||
1314 | } | ||
1315 | #else /* !defined (LAGRANGE_ENABLE_FRIBIDI) */ | ||
1316 | for (int pos = runVisual.start; pos < runVisual.end; pos++) { | 1335 | for (int pos = runVisual.start; pos < runVisual.end; pos++) { |
1317 | hb_buffer_add(hbBuf, visualText[pos], pos); | 1336 | hb_buffer_add(hbBuf, visualText[pos], pos); |
1318 | } | 1337 | } |
1319 | #endif | ||
1320 | hb_buffer_set_content_type(hbBuf, HB_BUFFER_CONTENT_TYPE_UNICODE); | 1338 | hb_buffer_set_content_type(hbBuf, HB_BUFFER_CONTENT_TYPE_UNICODE); |
1321 | hb_buffer_set_direction(hbBuf, HB_DIRECTION_LTR); | 1339 | hb_buffer_set_direction(hbBuf, HB_DIRECTION_LTR); |
1322 | /* hb_buffer_set_script(hbBuf, HB_SCRIPT_LATIN); */ /* will be autodetected */ | 1340 | /* hb_buffer_set_script(hbBuf, HB_SCRIPT_LATIN); */ /* will be autodetected */ |
@@ -1339,7 +1357,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1339 | if (isPictograph_Char(ch) || isEmoji_Char(ch)) { | 1357 | if (isPictograph_Char(ch) || isEmoji_Char(ch)) { |
1340 | const float dw = run->font->xScale * glyphPos[i].x_advance - monoAdvance; | 1358 | const float dw = run->font->xScale * glyphPos[i].x_advance - monoAdvance; |
1341 | glyphPos[i].x_offset -= dw / 2 / run->font->xScale - 1; | 1359 | glyphPos[i].x_offset -= dw / 2 / run->font->xScale - 1; |
1342 | glyphPos[i].x_advance -= dw / run->font->xScale - 1; | 1360 | glyphPos[i].x_advance -= dw / run->font->xScale - 1; |
1343 | } | 1361 | } |
1344 | } | 1362 | } |
1345 | } | 1363 | } |
@@ -1354,10 +1372,11 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1354 | const hb_glyph_info_t *info = &glyphInfo[i]; | 1372 | const hb_glyph_info_t *info = &glyphInfo[i]; |
1355 | const hb_codepoint_t glyphId = info->codepoint; | 1373 | const hb_codepoint_t glyphId = info->codepoint; |
1356 | // const char *textPos = sourceText.start + info->cluster; | 1374 | // const char *textPos = sourceText.start + info->cluster; |
1357 | const iGlyph *glyph = glyphByIndex_Font_(run->font, glyphId); | 1375 | const int visPos = info->cluster; |
1358 | const int glyphFlags = hb_glyph_info_get_glyph_flags(info); | 1376 | const iGlyph *glyph = glyphByIndex_Font_(run->font, glyphId); |
1359 | const float xOffset = run->font->xScale * glyphPos[i].x_offset; | 1377 | const int glyphFlags = hb_glyph_info_get_glyph_flags(info); |
1360 | const float xAdvance = run->font->xScale * glyphPos[i].x_advance; | 1378 | const float xOffset = run->font->xScale * glyphPos[i].x_offset; |
1379 | const float xAdvance = run->font->xScale * glyphPos[i].x_advance; | ||
1361 | if (args->wrap->mode == word_WrapTextMode) { | 1380 | if (args->wrap->mode == word_WrapTextMode) { |
1362 | /* When word wrapping, only consider certain places breakable. */ | 1381 | /* When word wrapping, only consider certain places breakable. */ |
1363 | const iChar ch = visualText[info->cluster]; | 1382 | const iChar ch = visualText[info->cluster]; |
@@ -1368,26 +1387,26 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1368 | } | 1387 | } |
1369 | else*/ | 1388 | else*/ |
1370 | if ((ch >= 128 || !ispunct(ch)) && (prevCh == '-' || prevCh == '/')) { | 1389 | if ((ch >= 128 || !ispunct(ch)) && (prevCh == '-' || prevCh == '/')) { |
1371 | safeBreak = i; | 1390 | safeBreak = visPos; |
1372 | // isSoftHyphenBreak = iFalse; | 1391 | // isSoftHyphenBreak = iFalse; |
1373 | } | 1392 | } |
1374 | else if (isSpace_Char(ch)) { | 1393 | else if (isSpace_Char(ch)) { |
1375 | safeBreak = i; | 1394 | safeBreak = visPos; |
1376 | // isSoftHyphenBreak = iFalse; | 1395 | // isSoftHyphenBreak = iFalse; |
1377 | } | 1396 | } |
1378 | prevCh = ch; | 1397 | prevCh = ch; |
1379 | } | 1398 | } |
1380 | else { | 1399 | else { |
1381 | if (~glyphFlags & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) { | 1400 | if (~glyphFlags & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) { |
1382 | safeBreak = i; | 1401 | safeBreak = visPos; |
1383 | } | 1402 | } |
1384 | } | 1403 | } |
1385 | if (x + xOffset + glyph->d[0].x + glyph->rect[0].size.x > args->wrap->maxWidth) { | 1404 | if (x + xOffset + glyph->d[0].x + glyph->rect[0].size.x > args->wrap->maxWidth) { |
1386 | if (safeBreak) { | 1405 | if (safeBreak >= 0) { |
1387 | breakPos = safeBreak + 1; | 1406 | breakPos = safeBreak; |
1388 | } | 1407 | } |
1389 | else { | 1408 | else { |
1390 | breakPos = i + 1; | 1409 | breakPos = visPos; |
1391 | } | 1410 | } |
1392 | break; | 1411 | break; |
1393 | } | 1412 | } |