diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-13 21:02:33 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-13 21:02:33 +0300 |
commit | d939dcd1109d7c92e6290976eaf84b94984abef3 (patch) | |
tree | 8e07462d687bec980095203e4d351b5e3a9774c0 | |
parent | f3a3b7785489f1d7c782e02d2e753d012729ab80 (diff) |
Drawing document RTL text runs
The base text direction of each line of text is determined when the document is laid out. When drawing runs, use this predetermined base direction.
-rw-r--r-- | src/gmdocument.c | 95 | ||||
-rw-r--r-- | src/gmdocument.h | 7 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 29 | ||||
-rw-r--r-- | src/ui/text.c | 54 | ||||
-rw-r--r-- | src/ui/text.h | 2 |
5 files changed, 104 insertions, 83 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c index 34987beb..9fc270d2 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -337,7 +337,7 @@ static enum iGmDocumentTheme currentTheme_(void) { | |||
337 | } | 337 | } |
338 | 338 | ||
339 | static void alignDecoration_GmRun_(iGmRun *run, iBool isCentered) { | 339 | static void alignDecoration_GmRun_(iGmRun *run, iBool isCentered) { |
340 | const iRect visBounds = visualBounds_Text(run->font, run->text); | 340 | const iRect visBounds = visualBounds_Text(run->textParams.font, run->text); |
341 | const int visWidth = width_Rect(visBounds); | 341 | const int visWidth = width_Rect(visBounds); |
342 | int xAdjust = 0; | 342 | int xAdjust = 0; |
343 | if (!isCentered) { | 343 | if (!isCentered) { |
@@ -414,12 +414,13 @@ static const int colors[max_GmLineType] = { | |||
414 | tmLinkText_ColorId, | 414 | tmLinkText_ColorId, |
415 | }; | 415 | }; |
416 | 416 | ||
417 | static iBool typesetOneLine_RunTypesetter_(iWrapText *wrap, iRangecc wrapRange, int origin, int advance) { | 417 | static iBool typesetOneLine_RunTypesetter_(iWrapText *wrap, iRangecc wrapRange, int origin, |
418 | int advance, iBool isBaseRTL) { | ||
418 | iAssert(wrapRange.start <= wrapRange.end); | 419 | iAssert(wrapRange.start <= wrapRange.end); |
419 | trimEnd_Rangecc(&wrapRange); | 420 | trimEnd_Rangecc(&wrapRange); |
420 | // printf("typeset: {%s}\n", cstr_Rangecc(wrapRange)); | 421 | // printf("typeset: {%s}\n", cstr_Rangecc(wrapRange)); |
421 | iRunTypesetter *d = wrap->context; | 422 | iRunTypesetter *d = wrap->context; |
422 | const int fontId = d->run.font; | 423 | const int fontId = d->run.textParams.font; |
423 | d->run.text = wrapRange; | 424 | d->run.text = wrapRange; |
424 | if (~d->run.flags & startOfLine_GmRunFlag && d->lineHeightReduction > 0.0f) { | 425 | if (~d->run.flags & startOfLine_GmRunFlag && d->lineHeightReduction > 0.0f) { |
425 | d->pos.y -= d->lineHeightReduction * lineHeight_Text(fontId); | 426 | d->pos.y -= d->lineHeightReduction * lineHeight_Text(fontId); |
@@ -431,6 +432,7 @@ static iBool typesetOneLine_RunTypesetter_(iWrapText *wrap, iRangecc wrapRange, | |||
431 | d->run.bounds.size.y = dims.y; | 432 | d->run.bounds.size.y = dims.y; |
432 | d->run.visBounds = d->run.bounds; | 433 | d->run.visBounds = d->run.bounds; |
433 | d->run.visBounds.size.x = dims.x; | 434 | d->run.visBounds.size.x = dims.x; |
435 | d->run.textParams.isRTL = isBaseRTL; | ||
434 | pushBack_Array(&d->layout, &d->run); | 436 | pushBack_Array(&d->layout, &d->run); |
435 | d->run.flags &= ~startOfLine_GmRunFlag; | 437 | d->run.flags &= ~startOfLine_GmRunFlag; |
436 | d->pos.y += lineHeight_Text(fontId) * prefs_App()->lineSpacing; | 438 | d->pos.y += lineHeight_Text(fontId) * prefs_App()->lineSpacing; |
@@ -520,7 +522,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
520 | if (*line.end == '\r') { | 522 | if (*line.end == '\r') { |
521 | line.end--; /* trim CR always */ | 523 | line.end--; /* trim CR always */ |
522 | } | 524 | } |
523 | iGmRun run = { .color = white_ColorId }; | 525 | iGmRun run = { .textParams = { .color = white_ColorId } }; |
524 | enum iGmLineType type; | 526 | enum iGmLineType type; |
525 | float indent = 0.0f; | 527 | float indent = 0.0f; |
526 | /* Detect the type of the line. */ | 528 | /* Detect the type of the line. */ |
@@ -565,7 +567,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
565 | } | 567 | } |
566 | } | 568 | } |
567 | trimLine_Rangecc(&line, type, isNormalized); | 569 | trimLine_Rangecc(&line, type, isNormalized); |
568 | run.font = fonts[type]; | 570 | run.textParams.font = fonts[type]; |
569 | /* Remember headings for the document outline. */ | 571 | /* Remember headings for the document outline. */ |
570 | if (type == heading1_GmLineType || type == heading2_GmLineType || type == heading3_GmLineType) { | 572 | if (type == heading1_GmLineType || type == heading2_GmLineType || type == heading3_GmLineType) { |
571 | pushBack_Array( | 573 | pushBack_Array( |
@@ -586,7 +588,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
586 | continue; | 588 | continue; |
587 | } | 589 | } |
588 | run.preId = preId; | 590 | run.preId = preId; |
589 | run.font = (d->format == plainText_SourceFormat ? regularMonospace_FontId : preFont); | 591 | run.textParams.font = (d->format == plainText_SourceFormat ? regularMonospace_FontId : preFont); |
590 | indent = indents[type]; | 592 | indent = indents[type]; |
591 | } | 593 | } |
592 | if (addSiteBanner) { | 594 | if (addSiteBanner) { |
@@ -601,9 +603,9 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
601 | banner.visBounds.size.y += iMaxi(6000 * lineHeight_Text(uiLabel_FontId) / | 603 | banner.visBounds.size.y += iMaxi(6000 * lineHeight_Text(uiLabel_FontId) / |
602 | d->size.x, lineHeight_Text(uiLabel_FontId) * 5); | 604 | d->size.x, lineHeight_Text(uiLabel_FontId) * 5); |
603 | } | 605 | } |
604 | banner.font = banner_FontId; | 606 | banner.text = bannerText; |
605 | banner.text = bannerText; | 607 | banner.textParams.font = banner_FontId; |
606 | banner.color = tmBannerTitle_ColorId; | 608 | banner.textParams.color = tmBannerTitle_ColorId; |
607 | pushBack_Array(&d->layout, &banner); | 609 | pushBack_Array(&d->layout, &banner); |
608 | pos.y += height_Rect(banner.visBounds) + | 610 | pos.y += height_Rect(banner.visBounds) + |
609 | lineHeight_Text(paragraph_FontId) * prefs->lineSpacing; | 611 | lineHeight_Text(paragraph_FontId) * prefs->lineSpacing; |
@@ -614,13 +616,13 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
614 | if (type == quote_GmLineType && !prefs->quoteIcon) { | 616 | if (type == quote_GmLineType && !prefs->quoteIcon) { |
615 | /* For quote indicators we still need to produce a run. */ | 617 | /* For quote indicators we still need to produce a run. */ |
616 | run.visBounds.pos = addX_I2(pos, indents[type] * gap_Text); | 618 | run.visBounds.pos = addX_I2(pos, indents[type] * gap_Text); |
617 | run.visBounds.size = init_I2(gap_Text, lineHeight_Text(run.font)); | 619 | run.visBounds.size = init_I2(gap_Text, lineHeight_Text(run.textParams.font)); |
618 | run.bounds = zero_Rect(); /* just visual */ | 620 | run.bounds = zero_Rect(); /* just visual */ |
619 | run.flags = quoteBorder_GmRunFlag | decoration_GmRunFlag; | 621 | run.flags = quoteBorder_GmRunFlag | decoration_GmRunFlag; |
620 | run.text = iNullRange; | 622 | run.text = iNullRange; |
621 | pushBack_Array(&d->layout, &run); | 623 | pushBack_Array(&d->layout, &run); |
622 | } | 624 | } |
623 | pos.y += lineHeight_Text(run.font) * prefs->lineSpacing; | 625 | pos.y += lineHeight_Text(run.textParams.font) * prefs->lineSpacing; |
624 | prevType = type; | 626 | prevType = type; |
625 | if (type != quote_GmLineType) { | 627 | if (type != quote_GmLineType) { |
626 | addQuoteIcon = prefs->quoteIcon; | 628 | addQuoteIcon = prefs->quoteIcon; |
@@ -664,13 +666,14 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
664 | const iGmPreMeta *meta = constAt_Array(&d->preMeta, preId - 1); | 666 | const iGmPreMeta *meta = constAt_Array(&d->preMeta, preId - 1); |
665 | if (meta->flags & folded_GmPreMetaFlag) { | 667 | if (meta->flags & folded_GmPreMetaFlag) { |
666 | const iBool isBlank = isEmpty_Range(&meta->altText); | 668 | const iBool isBlank = isEmpty_Range(&meta->altText); |
667 | iGmRun altText = { .font = paragraph_FontId, | 669 | iGmRun altText = { |
668 | .flags = (isBlank ? decoration_GmRunFlag : 0) | altText_GmRunFlag }; | 670 | .textParams = { .font = paragraph_FontId, .color = tmQuote_ColorId }, |
671 | .flags = (isBlank ? decoration_GmRunFlag : 0) | altText_GmRunFlag | ||
672 | }; | ||
669 | const iInt2 margin = preRunMargin_GmDocument(d, 0); | 673 | const iInt2 margin = preRunMargin_GmDocument(d, 0); |
670 | altText.color = tmQuote_ColorId; | ||
671 | altText.text = isBlank ? range_Lang(range_CStr("doc.pre.nocaption")) | 674 | altText.text = isBlank ? range_Lang(range_CStr("doc.pre.nocaption")) |
672 | : meta->altText; | 675 | : meta->altText; |
673 | iInt2 size = measureWrapRange_Text(altText.font, d->size.x - 2 * margin.x, | 676 | iInt2 size = measureWrapRange_Text(altText.textParams.font, d->size.x - 2 * margin.x, |
674 | altText.text).bounds.size; | 677 | altText.text).bounds.size; |
675 | altText.bounds = altText.visBounds = init_Rect(pos.x, pos.y, d->size.x, | 678 | altText.bounds = altText.visBounds = init_Rect(pos.x, pos.y, d->size.x, |
676 | size.y + 2 * margin.y); | 679 | size.y + 2 * margin.y); |
@@ -689,19 +692,19 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
689 | setRange_String(&d->title, line); | 692 | setRange_String(&d->title, line); |
690 | } | 693 | } |
691 | /* List bullet. */ | 694 | /* List bullet. */ |
692 | run.color = colors[type]; | 695 | run.textParams.color = colors[type]; |
693 | if (type == bullet_GmLineType) { | 696 | if (type == bullet_GmLineType) { |
694 | /* TODO: Literata bullet is broken? */ | 697 | /* TODO: Literata bullet is broken? */ |
695 | iGmRun bulRun = run; | 698 | iGmRun bulRun = run; |
696 | if (prefs->font == literata_TextFont) { | 699 | if (prefs->font == literata_TextFont) { |
697 | /* Something wrong this the glyph in Literata, looks cropped. */ | 700 | /* Something wrong this the glyph in Literata, looks cropped. */ |
698 | bulRun.font = defaultContentRegular_FontId; | 701 | bulRun.textParams.font = defaultContentRegular_FontId; |
699 | } | 702 | } |
700 | bulRun.color = tmQuote_ColorId; | 703 | bulRun.textParams.color = tmQuote_ColorId; |
701 | bulRun.visBounds.pos = addX_I2(pos, (indents[text_GmLineType] - 0.55f) * gap_Text); | 704 | bulRun.visBounds.pos = addX_I2(pos, (indents[text_GmLineType] - 0.55f) * gap_Text); |
702 | bulRun.visBounds.size = | 705 | bulRun.visBounds.size = |
703 | init_I2((indents[bullet_GmLineType] - indents[text_GmLineType]) * gap_Text, | 706 | init_I2((indents[bullet_GmLineType] - indents[text_GmLineType]) * gap_Text, |
704 | lineHeight_Text(bulRun.font)); | 707 | lineHeight_Text(bulRun.textParams.font)); |
705 | // bulRun.visBounds.pos.x -= 4 * gap_Text - width_Rect(bulRun.visBounds) / 2; | 708 | // bulRun.visBounds.pos.x -= 4 * gap_Text - width_Rect(bulRun.visBounds) / 2; |
706 | bulRun.bounds = zero_Rect(); /* just visual */ | 709 | bulRun.bounds = zero_Rect(); /* just visual */ |
707 | bulRun.text = range_CStr(bullet); | 710 | bulRun.text = range_CStr(bullet); |
@@ -713,11 +716,11 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
713 | if (type == quote_GmLineType && addQuoteIcon) { | 716 | if (type == quote_GmLineType && addQuoteIcon) { |
714 | addQuoteIcon = iFalse; | 717 | addQuoteIcon = iFalse; |
715 | iGmRun quoteRun = run; | 718 | iGmRun quoteRun = run; |
716 | quoteRun.font = heading1_FontId; | 719 | quoteRun.textParams.font = heading1_FontId; |
717 | quoteRun.text = range_CStr(quote); | 720 | quoteRun.text = range_CStr(quote); |
718 | quoteRun.color = tmQuoteIcon_ColorId; | 721 | quoteRun.textParams.color = tmQuoteIcon_ColorId; |
719 | iRect vis = visualBounds_Text(quoteRun.font, quoteRun.text); | 722 | iRect vis = visualBounds_Text(quoteRun.textParams.font, quoteRun.text); |
720 | quoteRun.visBounds.size = measure_Text(quoteRun.font, quote).bounds.size; | 723 | quoteRun.visBounds.size = measure_Text(quoteRun.textParams.font, quote).bounds.size; |
721 | quoteRun.visBounds.pos = | 724 | quoteRun.visBounds.pos = |
722 | add_I2(pos, | 725 | add_I2(pos, |
723 | init_I2((indents[quote_GmLineType] - 5) * gap_Text, | 726 | init_I2((indents[quote_GmLineType] - 5) * gap_Text, |
@@ -733,7 +736,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
733 | if (type == link_GmLineType) { | 736 | if (type == link_GmLineType) { |
734 | iGmRun icon = run; | 737 | iGmRun icon = run; |
735 | icon.visBounds.pos = pos; | 738 | icon.visBounds.pos = pos; |
736 | icon.visBounds.size = init_I2(indent * gap_Text, lineHeight_Text(run.font)); | 739 | icon.visBounds.size = init_I2(indent * gap_Text, lineHeight_Text(run.textParams.font)); |
737 | icon.bounds = zero_Rect(); /* just visual */ | 740 | icon.bounds = zero_Rect(); /* just visual */ |
738 | const iGmLink *link = constAt_PtrArray(&d->links, run.linkId - 1); | 741 | const iGmLink *link = constAt_PtrArray(&d->links, run.linkId - 1); |
739 | icon.text = range_CStr(link->flags & query_GmLinkFlag ? magnifyingGlass | 742 | icon.text = range_CStr(link->flags & query_GmLinkFlag ? magnifyingGlass |
@@ -748,23 +751,23 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
748 | } | 751 | } |
749 | /* TODO: List bullets needs the same centering logic. */ | 752 | /* TODO: List bullets needs the same centering logic. */ |
750 | /* Special exception for the tiny bullet operator. */ | 753 | /* Special exception for the tiny bullet operator. */ |
751 | icon.font = equal_Rangecc(link->labelIcon, "\u2219") ? regularMonospace_FontId | 754 | icon.textParams.font = equal_Rangecc(link->labelIcon, "\u2219") ? regularMonospace_FontId |
752 | : regular_FontId; | 755 | : regular_FontId; |
753 | alignDecoration_GmRun_(&icon, iFalse); | 756 | alignDecoration_GmRun_(&icon, iFalse); |
754 | icon.color = linkColor_GmDocument(d, run.linkId, icon_GmLinkPart); | 757 | icon.textParams.color = linkColor_GmDocument(d, run.linkId, icon_GmLinkPart); |
755 | icon.flags |= decoration_GmRunFlag; | 758 | icon.flags |= decoration_GmRunFlag; |
756 | pushBack_Array(&d->layout, &icon); | 759 | pushBack_Array(&d->layout, &icon); |
757 | } | 760 | } |
758 | run.color = colors[type]; | 761 | run.textParams.color = colors[type]; |
759 | if (d->format == plainText_SourceFormat) { | 762 | if (d->format == plainText_SourceFormat) { |
760 | run.color = colors[text_GmLineType]; | 763 | run.textParams.color = colors[text_GmLineType]; |
761 | } | 764 | } |
762 | /* Special formatting for the first paragraph (e.g., subtitle, introduction, or lede). */ | 765 | /* Special formatting for the first paragraph (e.g., subtitle, introduction, or lede). */ |
763 | // int bigCount = 0; | 766 | // int bigCount = 0; |
764 | iBool isLedeParagraph = iFalse; | 767 | iBool isLedeParagraph = iFalse; |
765 | if (type == text_GmLineType && isFirstText) { | 768 | if (type == text_GmLineType && isFirstText) { |
766 | if (!isMono) run.font = firstParagraph_FontId; | 769 | if (!isMono) run.textParams.font = firstParagraph_FontId; |
767 | run.color = tmFirstParagraph_ColorId; | 770 | run.textParams.color = tmFirstParagraph_ColorId; |
768 | // bigCount = 15; /* max lines -- what if the whole document is one paragraph? */ | 771 | // bigCount = 15; /* max lines -- what if the whole document is one paragraph? */ |
769 | isLedeParagraph = iTrue; | 772 | isLedeParagraph = iTrue; |
770 | isFirstText = iFalse; | 773 | isFirstText = iFalse; |
@@ -811,7 +814,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
811 | } | 814 | } |
812 | /* Visited links are never bold. */ | 815 | /* Visited links are never bold. */ |
813 | if (run.linkId && linkFlags_GmDocument(d, run.linkId) & visited_GmLinkFlag) { | 816 | if (run.linkId && linkFlags_GmDocument(d, run.linkId) & visited_GmLinkFlag) { |
814 | rts.run.font = paragraph_FontId; | 817 | rts.run.textParams.font = paragraph_FontId; |
815 | } | 818 | } |
816 | } | 819 | } |
817 | if (!prefs->quoteIcon && type == quote_GmLineType) { | 820 | if (!prefs->quoteIcon && type == quote_GmLineType) { |
@@ -827,15 +830,15 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
827 | .mode = word_WrapTextMode, | 830 | .mode = word_WrapTextMode, |
828 | .wrapFunc = typesetOneLine_RunTypesetter_, | 831 | .wrapFunc = typesetOneLine_RunTypesetter_, |
829 | .context = &rts }, | 832 | .context = &rts }, |
830 | run.font); | 833 | run.textParams.font); |
831 | if (!isLedeParagraph || size_Array(&rts.layout) <= maxLedeLines_) { | 834 | if (!isLedeParagraph || size_Array(&rts.layout) <= maxLedeLines_) { |
832 | commit_RunTypesetter_(&rts, d); | 835 | commit_RunTypesetter_(&rts, d); |
833 | break; | 836 | break; |
834 | } | 837 | } |
835 | clear_RunTypesetter_(&rts); | 838 | clear_RunTypesetter_(&rts); |
836 | rts.pos = pos; | 839 | rts.pos = pos; |
837 | rts.run.font = rts.fonts[text_GmLineType]; | 840 | rts.run.textParams.font = rts.fonts[text_GmLineType]; |
838 | rts.run.color = colors [text_GmLineType]; | 841 | rts.run.textParams.color = colors[text_GmLineType]; |
839 | isLedeParagraph = iFalse; | 842 | isLedeParagraph = iFalse; |
840 | } | 843 | } |
841 | pos = rts.pos; | 844 | pos = rts.pos; |
@@ -869,11 +872,11 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
869 | run.visBounds.pos.x = run.bounds.size.x / 2 - width_Rect(run.visBounds) / 2; | 872 | run.visBounds.pos.x = run.bounds.size.x / 2 - width_Rect(run.visBounds) / 2; |
870 | run.bounds.size.y = run.visBounds.size.y; | 873 | run.bounds.size.y = run.visBounds.size.y; |
871 | } | 874 | } |
872 | run.text = iNullRange; | 875 | run.text = iNullRange; |
873 | run.font = 0; | 876 | run.textParams.font = 0; |
874 | run.color = 0; | 877 | run.textParams.color = 0; |
875 | run.mediaType = image_GmRunMediaType; | 878 | run.mediaType = image_GmRunMediaType; |
876 | run.mediaId = imageId; | 879 | run.mediaId = imageId; |
877 | pushBack_Array(&d->layout, &run); | 880 | pushBack_Array(&d->layout, &run); |
878 | pos.y += run.bounds.size.y + margin; | 881 | pos.y += run.bounds.size.y + margin; |
879 | } | 882 | } |
@@ -888,7 +891,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
888 | run.bounds.size.y = lineHeight_Text(uiContent_FontId) + 3 * gap_UI; | 891 | run.bounds.size.y = lineHeight_Text(uiContent_FontId) + 3 * gap_UI; |
889 | run.visBounds = run.bounds; | 892 | run.visBounds = run.bounds; |
890 | run.text = iNullRange; | 893 | run.text = iNullRange; |
891 | run.color = 0; | 894 | run.textParams.color = 0; |
892 | run.mediaType = audio_GmRunMediaType; | 895 | run.mediaType = audio_GmRunMediaType; |
893 | run.mediaId = audioId; | 896 | run.mediaId = audioId; |
894 | pushBack_Array(&d->layout, &run); | 897 | pushBack_Array(&d->layout, &run); |
@@ -905,7 +908,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
905 | run.bounds.size.y = 2 * lineHeight_Text(uiContent_FontId) + 4 * gap_UI; | 908 | run.bounds.size.y = 2 * lineHeight_Text(uiContent_FontId) + 4 * gap_UI; |
906 | run.visBounds = run.bounds; | 909 | run.visBounds = run.bounds; |
907 | run.text = iNullRange; | 910 | run.text = iNullRange; |
908 | run.color = 0; | 911 | run.textParams.color = 0; |
909 | run.mediaType = download_GmRunMediaType; | 912 | run.mediaType = download_GmRunMediaType; |
910 | run.mediaId = downloadId; | 913 | run.mediaId = downloadId; |
911 | pushBack_Array(&d->layout, &run); | 914 | pushBack_Array(&d->layout, &run); |
@@ -1517,11 +1520,11 @@ static void markLinkRunsVisited_GmDocument_(iGmDocument *d, const iIntSet *linkI | |||
1517 | iForEach(Array, r, &d->layout) { | 1520 | iForEach(Array, r, &d->layout) { |
1518 | iGmRun *run = r.value; | 1521 | iGmRun *run = r.value; |
1519 | if (run->linkId && !run->mediaId && contains_IntSet(linkIds, run->linkId)) { | 1522 | if (run->linkId && !run->mediaId && contains_IntSet(linkIds, run->linkId)) { |
1520 | if (run->font == bold_FontId) { | 1523 | if (run->textParams.font == bold_FontId) { |
1521 | run->font = paragraph_FontId; | 1524 | run->textParams.font = paragraph_FontId; |
1522 | } | 1525 | } |
1523 | else if (run->flags & decoration_GmRunFlag) { | 1526 | else if (run->flags & decoration_GmRunFlag) { |
1524 | run->color = linkColor_GmDocument(d, run->linkId, icon_GmLinkPart); | 1527 | run->textParams.color = linkColor_GmDocument(d, run->linkId, icon_GmLinkPart); |
1525 | } | 1528 | } |
1526 | } | 1529 | } |
1527 | } | 1530 | } |
@@ -2008,7 +2011,7 @@ iRangecc findLoc_GmRun(const iGmRun *d, iInt2 pos) { | |||
2008 | return (iRangecc){ d->text.start, d->text.start }; | 2011 | return (iRangecc){ d->text.start, d->text.start }; |
2009 | } | 2012 | } |
2010 | iRangecc loc; | 2013 | iRangecc loc; |
2011 | tryAdvanceNoWrap_Text(d->font, d->text, x, &loc.start); | 2014 | tryAdvanceNoWrap_Text(d->textParams.font, d->text, x, &loc.start); |
2012 | loc.end = loc.start; | 2015 | loc.end = loc.start; |
2013 | iChar ch; | 2016 | iChar ch; |
2014 | if (d->text.end != loc.start) { | 2017 | if (d->text.end != loc.start) { |
diff --git a/src/gmdocument.h b/src/gmdocument.h index 0d50e6ad..9d906006 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h | |||
@@ -128,8 +128,11 @@ struct Impl_GmRun { | |||
128 | iRangecc text; | 128 | iRangecc text; |
129 | iRect bounds; /* used for hit testing, may extend to edges */ | 129 | iRect bounds; /* used for hit testing, may extend to edges */ |
130 | iRect visBounds; /* actual visual bounds */ | 130 | iRect visBounds; /* actual visual bounds */ |
131 | uint8_t font; | 131 | struct { |
132 | uint8_t color; | 132 | uint16_t color : 8; |
133 | uint16_t font : 7; | ||
134 | uint16_t isRTL : 1; | ||
135 | } textParams; | ||
133 | uint8_t flags; | 136 | uint8_t flags; |
134 | uint8_t mediaType; | 137 | uint8_t mediaType; |
135 | uint16_t preId; /* preformatted block ID (sequential) */ | 138 | uint16_t preId; /* preformatted block ID (sequential) */ |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 3c6c0039..ea4909eb 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -1585,7 +1585,7 @@ static void parseUser_DocumentWidget_(iDocumentWidget *d) { | |||
1585 | static void cacheRunGlyphs_(void *data, const iGmRun *run) { | 1585 | static void cacheRunGlyphs_(void *data, const iGmRun *run) { |
1586 | iUnused(data); | 1586 | iUnused(data); |
1587 | if (!isEmpty_Range(&run->text)) { | 1587 | if (!isEmpty_Range(&run->text)) { |
1588 | cache_Text(run->font, run->text); | 1588 | cache_Text(run->textParams.font, run->text); |
1589 | } | 1589 | } |
1590 | } | 1590 | } |
1591 | 1591 | ||
@@ -3910,14 +3910,14 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol | |||
3910 | contains_Range(&mark, run->text.start))) { | 3910 | contains_Range(&mark, run->text.start))) { |
3911 | int x = 0; | 3911 | int x = 0; |
3912 | if (!*isInside) { | 3912 | if (!*isInside) { |
3913 | x = measureRange_Text(run->font, | 3913 | x = measureRange_Text(run->textParams.font, |
3914 | (iRangecc){ run->text.start, iMax(run->text.start, mark.start) }) | 3914 | (iRangecc){ run->text.start, iMax(run->text.start, mark.start) }) |
3915 | .advance.x; | 3915 | .advance.x; |
3916 | } | 3916 | } |
3917 | int w = width_Rect(run->visBounds) - x; | 3917 | int w = width_Rect(run->visBounds) - x; |
3918 | if (contains_Range(&run->text, mark.end) || mark.end < run->text.start) { | 3918 | if (contains_Range(&run->text, mark.end) || mark.end < run->text.start) { |
3919 | w = measureRange_Text( | 3919 | w = measureRange_Text( |
3920 | run->font, | 3920 | run->textParams.font, |
3921 | !*isInside ? mark | 3921 | !*isInside ? mark |
3922 | : (iRangecc){ run->text.start, iMax(run->text.start, mark.end) }) | 3922 | : (iRangecc){ run->text.start, iMax(run->text.start, mark.end) }) |
3923 | .advance.x; | 3923 | .advance.x; |
@@ -3973,15 +3973,15 @@ static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2 | |||
3973 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); | 3973 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); |
3974 | if (icon) { | 3974 | if (icon) { |
3975 | appendChar_String(&str, icon); | 3975 | appendChar_String(&str, icon); |
3976 | const iRect iconRect = visualBounds_Text(run->font, range_String(&str)); | 3976 | const iRect iconRect = visualBounds_Text(run->textParams.font, range_String(&str)); |
3977 | drawRange_Text( | 3977 | drawRange_Text( |
3978 | run->font, | 3978 | run->textParams.font, |
3979 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2), | 3979 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->textParams.font) / 2), |
3980 | tmBannerIcon_ColorId, | 3980 | tmBannerIcon_ColorId, |
3981 | range_String(&str)); | 3981 | range_String(&str)); |
3982 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; | 3982 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; |
3983 | } | 3983 | } |
3984 | drawRange_Text(run->font, | 3984 | drawRange_Text(run->textParams.font, |
3985 | bpos, | 3985 | bpos, |
3986 | tmBannerTitle_ColorId, | 3986 | tmBannerTitle_ColorId, |
3987 | bannerText_DocumentWidget_(d->widget)); | 3987 | bannerText_DocumentWidget_(d->widget)); |
@@ -4088,7 +4088,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4088 | /* Media UIs are drawn afterwards as a dynamic overlay. */ | 4088 | /* Media UIs are drawn afterwards as a dynamic overlay. */ |
4089 | return; | 4089 | return; |
4090 | } | 4090 | } |
4091 | enum iColorId fg = run->color; | 4091 | enum iColorId fg = run->textParams.color; |
4092 | const iGmDocument *doc = d->widget->doc; | 4092 | const iGmDocument *doc = d->widget->doc; |
4093 | const int linkFlags = linkFlags_GmDocument(doc, run->linkId); | 4093 | const int linkFlags = linkFlags_GmDocument(doc, run->linkId); |
4094 | /* Hover state of a link. */ | 4094 | /* Hover state of a link. */ |
@@ -4152,8 +4152,11 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4152 | const iInt2 margin = preRunMargin_GmDocument(doc, run->preId); | 4152 | const iInt2 margin = preRunMargin_GmDocument(doc, run->preId); |
4153 | fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId); | 4153 | fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId); |
4154 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId); | 4154 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId); |
4155 | drawWrapRange_Text(run->font, add_I2(visPos, margin), | 4155 | drawWrapRange_Text(run->textParams.font, |
4156 | run->visBounds.size.x - 2 * margin.x, run->color, run->text); | 4156 | add_I2(visPos, margin), |
4157 | run->visBounds.size.x - 2 * margin.x, | ||
4158 | run->textParams.color, | ||
4159 | run->text); | ||
4157 | } | 4160 | } |
4158 | else if (run->flags & siteBanner_GmRunFlag) { | 4161 | else if (run->flags & siteBanner_GmRunFlag) { |
4159 | /* Banner background. */ | 4162 | /* Banner background. */ |
@@ -4194,7 +4197,11 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4194 | height_Rect(run->visBounds), | 4197 | height_Rect(run->visBounds), |
4195 | tmQuoteIcon_ColorId); | 4198 | tmQuoteIcon_ColorId); |
4196 | } | 4199 | } |
4197 | drawBoundRange_Text(run->font, visPos, width_Rect(run->visBounds), fg, run->text); | 4200 | drawBoundRange_Text(run->textParams.font, |
4201 | visPos, | ||
4202 | (run->textParams.isRTL ? -1 : 1) * width_Rect(run->visBounds), | ||
4203 | fg, | ||
4204 | run->text); | ||
4198 | runDrawn:; | 4205 | runDrawn:; |
4199 | } | 4206 | } |
4200 | /* Presentation of links. */ | 4207 | /* Presentation of links. */ |
diff --git a/src/ui/text.c b/src/ui/text.c index e1638368..03ce46c7 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -792,7 +792,8 @@ struct Impl_AttributedRun { | |||
792 | }; | 792 | }; |
793 | 793 | ||
794 | iDeclareType(AttributedText) | 794 | iDeclareType(AttributedText) |
795 | iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, iColor fgColor) | 795 | iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, |
796 | iColor fgColor, int baseDir) | ||
796 | 797 | ||
797 | struct Impl_AttributedText { | 798 | struct Impl_AttributedText { |
798 | iRangecc source; /* original source text */ | 799 | iRangecc source; /* original source text */ |
@@ -809,8 +810,9 @@ struct Impl_AttributedText { | |||
809 | iBool isBaseRTL; | 810 | iBool isBaseRTL; |
810 | }; | 811 | }; |
811 | 812 | ||
812 | iDefineTypeConstructionArgs(AttributedText, (iRangecc text, size_t maxLen, iFont *font, iColor fgColor), | 813 | iDefineTypeConstructionArgs(AttributedText, |
813 | text, maxLen, font, fgColor) | 814 | (iRangecc text, size_t maxLen, iFont *font, iColor fgColor, int baseDir), |
815 | text, maxLen, font, fgColor, baseDir) | ||
814 | 816 | ||
815 | static const char *sourcePtr_AttributedText_(const iAttributedText *d, int logicalPos) { | 817 | static const char *sourcePtr_AttributedText_(const iAttributedText *d, int logicalPos) { |
816 | const int *logToSource = constData_Array(&d->logicalToSourceOffset); | 818 | const int *logToSource = constData_Array(&d->logicalToSourceOffset); |
@@ -867,7 +869,7 @@ static enum iFontId fontId_Text_(const iFont *font) { | |||
867 | return (enum iFontId) (font - text_.fonts); | 869 | return (enum iFontId) (font - text_.fonts); |
868 | } | 870 | } |
869 | 871 | ||
870 | static void prepare_AttributedText_(iAttributedText *d) { | 872 | static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir) { |
871 | iAssert(isEmpty_Array(&d->runs)); | 873 | iAssert(isEmpty_Array(&d->runs)); |
872 | size_t length = 0; | 874 | size_t length = 0; |
873 | /* Prepare the UTF-32 logical string. */ { | 875 | /* Prepare the UTF-32 logical string. */ { |
@@ -896,7 +898,7 @@ static void prepare_AttributedText_(iAttributedText *d) { | |||
896 | data_Array(&d->logicalToVisual), | 898 | data_Array(&d->logicalToVisual), |
897 | data_Array(&d->visualToLogical), | 899 | data_Array(&d->visualToLogical), |
898 | (FriBidiLevel *) d->bidiLevels); | 900 | (FriBidiLevel *) d->bidiLevels); |
899 | d->isBaseRTL = FRIBIDI_IS_RTL(baseDir); | 901 | d->isBaseRTL = (overrideBaseDir == 0 ? FRIBIDI_IS_RTL(baseDir) : (overrideBaseDir < 0)); |
900 | #endif | 902 | #endif |
901 | } | 903 | } |
902 | /* The mapping needs to include the terminating NULL position. */ { | 904 | /* The mapping needs to include the terminating NULL position. */ { |
@@ -1017,7 +1019,8 @@ static void prepare_AttributedText_(iAttributedText *d) { | |||
1017 | #endif | 1019 | #endif |
1018 | } | 1020 | } |
1019 | 1021 | ||
1020 | void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, iColor fgColor) { | 1022 | void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, iColor fgColor, |
1023 | int baseDir) { | ||
1021 | d->source = text; | 1024 | d->source = text; |
1022 | d->maxLen = maxLen ? maxLen : iInvalidSize; | 1025 | d->maxLen = maxLen ? maxLen : iInvalidSize; |
1023 | d->font = font; | 1026 | d->font = font; |
@@ -1030,7 +1033,7 @@ void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont | |||
1030 | init_Array(&d->logicalToSourceOffset, sizeof(int)); | 1033 | init_Array(&d->logicalToSourceOffset, sizeof(int)); |
1031 | d->bidiLevels = NULL; | 1034 | d->bidiLevels = NULL; |
1032 | d->isBaseRTL = iFalse; | 1035 | d->isBaseRTL = iFalse; |
1033 | prepare_AttributedText_(d); | 1036 | prepare_AttributedText_(d, baseDir); |
1034 | } | 1037 | } |
1035 | 1038 | ||
1036 | void deinit_AttributedText(iAttributedText *d) { | 1039 | void deinit_AttributedText(iAttributedText *d) { |
@@ -1181,7 +1184,7 @@ static void cacheTextGlyphs_Font_(iFont *d, const iRangecc text) { | |||
1181 | iArray glyphIndices; | 1184 | iArray glyphIndices; |
1182 | init_Array(&glyphIndices, sizeof(uint32_t)); | 1185 | init_Array(&glyphIndices, sizeof(uint32_t)); |
1183 | iAttributedText attrText; | 1186 | iAttributedText attrText; |
1184 | init_AttributedText(&attrText, text, 0, d, (iColor){}); | 1187 | init_AttributedText(&attrText, text, 0, d, (iColor){}, 0); |
1185 | /* We use AttributedText here so the font lookup matches the behavior during text drawing -- | 1188 | /* We use AttributedText here so the font lookup matches the behavior during text drawing -- |
1186 | glyphs may be selected from a font that's different than `d`. */ | 1189 | glyphs may be selected from a font that's different than `d`. */ |
1187 | const iChar *logicalText = constData_Array(&attrText.logical); | 1190 | const iChar *logicalText = constData_Array(&attrText.logical); |
@@ -1229,9 +1232,10 @@ struct Impl_RunArgs { | |||
1229 | size_t maxLen; /* max characters to process */ | 1232 | size_t maxLen; /* max characters to process */ |
1230 | iInt2 pos; | 1233 | iInt2 pos; |
1231 | iWrapText * wrap; | 1234 | iWrapText * wrap; |
1232 | int xposLimit; /* hard limit for wrapping */ | 1235 | int xposLimit; /* hard limit for wrapping */ |
1233 | int xposLayoutBound; /* visible bound for layout purposes; does not affect wrapping */ | 1236 | int xposLayoutBound; /* visible bound for layout purposes; does not affect wrapping */ |
1234 | int color; | 1237 | int color; |
1238 | int baseDir; | ||
1235 | /* TODO: Cleanup using TextMetrics | 1239 | /* TODO: Cleanup using TextMetrics |
1236 | Use TextMetrics output pointer instead of return value & cursorAdvance_out. */ | 1240 | Use TextMetrics output pointer instead of return value & cursorAdvance_out. */ |
1237 | iInt2 * cursorAdvance_out; | 1241 | iInt2 * cursorAdvance_out; |
@@ -1239,13 +1243,13 @@ struct Impl_RunArgs { | |||
1239 | int * runAdvance_out; | 1243 | int * runAdvance_out; |
1240 | }; | 1244 | }; |
1241 | 1245 | ||
1242 | static iBool notify_WrapText_(iWrapText *d, const char *ending, int origin, int advance) { | 1246 | static iBool notify_WrapText_(iWrapText *d, const char *ending, int origin, int advance, iBool isBaseRTL) { |
1243 | if (d && d->wrapFunc && d->wrapRange_.start) { | 1247 | if (d && d->wrapFunc && d->wrapRange_.start) { |
1244 | /* `wrapRange_` uses logical indices. */ | 1248 | /* `wrapRange_` uses logical indices. */ |
1245 | const char *end = ending ? ending : d->wrapRange_.end; | 1249 | const char *end = ending ? ending : d->wrapRange_.end; |
1246 | iRangecc range = { d->wrapRange_.start, end }; | 1250 | iRangecc range = { d->wrapRange_.start, end }; |
1247 | iAssert(range.start <= range.end); | 1251 | iAssert(range.start <= range.end); |
1248 | const iBool result = d->wrapFunc(d, range, origin, advance); | 1252 | const iBool result = d->wrapFunc(d, range, origin, advance, isBaseRTL); |
1249 | if (result) { | 1253 | if (result) { |
1250 | d->wrapRange_.start = end; | 1254 | d->wrapRange_.start = end; |
1251 | } | 1255 | } |
@@ -1380,7 +1384,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1380 | font is used and other attributes such as color. (HarfBuzz shaping is done | 1384 | font is used and other attributes such as color. (HarfBuzz shaping is done |
1381 | with one specific font.) */ | 1385 | with one specific font.) */ |
1382 | iAttributedText attrText; | 1386 | iAttributedText attrText; |
1383 | init_AttributedText(&attrText, args->text, args->maxLen, d, get_Color(args->color)); | 1387 | init_AttributedText(&attrText, args->text, args->maxLen, d, get_Color(args->color), |
1388 | args->baseDir); | ||
1384 | if (args->wrap) { | 1389 | if (args->wrap) { |
1385 | /* TODO: Duplicated args? */ | 1390 | /* TODO: Duplicated args? */ |
1386 | iAssert(equalRange_Rangecc(args->wrap->text, args->text)); | 1391 | iAssert(equalRange_Rangecc(args->wrap->text, args->text)); |
@@ -1390,7 +1395,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1390 | const iChar *logicalText = constData_Array(&attrText.logical); | 1395 | const iChar *logicalText = constData_Array(&attrText.logical); |
1391 | const iChar *visualText = constData_Array(&attrText.visual); | 1396 | const iChar *visualText = constData_Array(&attrText.visual); |
1392 | const int * logToVis = constData_Array(&attrText.logicalToVisual); | 1397 | const int * logToVis = constData_Array(&attrText.logicalToVisual); |
1393 | const int * visToLog = constData_Array(&attrText.visualToLogical); | 1398 | // const int * visToLog = constData_Array(&attrText.visualToLogical); |
1394 | const size_t runCount = size_Array(&attrText.runs); | 1399 | const size_t runCount = size_Array(&attrText.runs); |
1395 | iArray buffers; | 1400 | iArray buffers; |
1396 | init_Array(&buffers, sizeof(iGlyphBuffer)); | 1401 | init_Array(&buffers, sizeof(iGlyphBuffer)); |
@@ -1580,7 +1585,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1580 | if (!notify_WrapText_(args->wrap, | 1585 | if (!notify_WrapText_(args->wrap, |
1581 | sourcePtr_AttributedText_(&attrText, wrapResumePos), | 1586 | sourcePtr_AttributedText_(&attrText, wrapResumePos), |
1582 | origin, | 1587 | origin, |
1583 | iRound(wrapAdvance))) { | 1588 | iRound(wrapAdvance), |
1589 | attrText.isBaseRTL)) { | ||
1584 | willAbortDueToWrap = iTrue; | 1590 | willAbortDueToWrap = iTrue; |
1585 | } | 1591 | } |
1586 | xCursor = origin; | 1592 | xCursor = origin; |
@@ -1743,8 +1749,9 @@ static int runFlagsFromId_(enum iFontId fontId) { | |||
1743 | return runFlags; | 1749 | return runFlags; |
1744 | } | 1750 | } |
1745 | 1751 | ||
1746 | static iBool cbAdvanceOneLine_(iWrapText *d, iRangecc range, int origin, int advance) { | 1752 | static iBool cbAdvanceOneLine_(iWrapText *d, iRangecc range, int origin, int advance, |
1747 | iUnused(origin, advance); | 1753 | iBool isBaseRTL) { |
1754 | iUnused(origin, advance, isBaseRTL); | ||
1748 | *((const char **) d->context) = range.end; | 1755 | *((const char **) d->context) = range.end; |
1749 | return iFalse; /* just one line */ | 1756 | return iFalse; /* just one line */ |
1750 | } | 1757 | } |
@@ -1785,9 +1792,9 @@ iTextMetrics measureN_Text(int fontId, const char *text, size_t n) { | |||
1785 | } | 1792 | } |
1786 | 1793 | ||
1787 | static void drawBoundedN_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text, size_t maxLen) { | 1794 | static void drawBoundedN_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text, size_t maxLen) { |
1788 | iText *d = &text_; | 1795 | iText * d = &text_; |
1789 | iFont *font = font_Text_(fontId); | 1796 | iFont * font = font_Text_(fontId); |
1790 | const iColor clr = get_Color(color & mask_ColorId); | 1797 | const iColor clr = get_Color(color & mask_ColorId); |
1791 | SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b); | 1798 | SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b); |
1792 | run_Font_(font, | 1799 | run_Font_(font, |
1793 | &(iRunArgs){ .mode = draw_RunMode | | 1800 | &(iRunArgs){ .mode = draw_RunMode | |
@@ -1798,7 +1805,8 @@ static void drawBoundedN_Text_(int fontId, iInt2 pos, int xposBound, int color, | |||
1798 | .maxLen = maxLen, | 1805 | .maxLen = maxLen, |
1799 | .pos = pos, | 1806 | .pos = pos, |
1800 | .xposLayoutBound = xposBound, | 1807 | .xposLayoutBound = xposBound, |
1801 | .color = color & mask_ColorId }); | 1808 | .color = color & mask_ColorId, |
1809 | .baseDir = xposBound ? iSign(xposBound - pos.x) : 0 }); | ||
1802 | } | 1810 | } |
1803 | 1811 | ||
1804 | static void drawBounded_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text) { | 1812 | static void drawBounded_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text) { |
diff --git a/src/ui/text.h b/src/ui/text.h index 341f94da..4630b9f6 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -205,7 +205,7 @@ struct Impl_WrapText { | |||
205 | iRangecc text; | 205 | iRangecc text; |
206 | int maxWidth; | 206 | int maxWidth; |
207 | enum iWrapTextMode mode; | 207 | enum iWrapTextMode mode; |
208 | iBool (*wrapFunc)(iWrapText *, iRangecc wrappedText, int origin, int advance); | 208 | iBool (*wrapFunc)(iWrapText *, iRangecc wrappedText, int origin, int advance, iBool isBaseRTL); |
209 | void * context; | 209 | void * context; |
210 | /* internal */ | 210 | /* internal */ |
211 | iRangecc wrapRange_; | 211 | iRangecc wrapRange_; |