summaryrefslogtreecommitdiff
path: root/src/ui/text.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-07-04 17:59:05 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-07-04 17:59:05 +0300
commit9ffc5aeb1349bb954582278e3e6f4d9a824f4284 (patch)
tree23359ac4633a4dbbf0a82ed50144bd5936745992 /src/ui/text.c
parent68b67c1de0754a763a138377b2b1250313a7eaf0 (diff)
Text: Apply FriBidi (again); fixed wrapping bugs
Diffstat (limited to 'src/ui/text.c')
-rw-r--r--src/ui/text.c157
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
796iDefineTypeConstructionArgs(AttributedText, (iRangecc text, size_t maxLen, iFont *font, iColor fgColor), 797iDefineTypeConstructionArgs(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
949void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, iColor fgColor) { 1012void 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
960void deinit_AttributedText(iAttributedText *d) { 1024void 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 }