diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-10-15 12:08:27 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-10-15 12:08:27 +0300 |
commit | 2f3987f5e54d95658f95c6991b0644bc15eedabf (patch) | |
tree | c8b76534a2b5d758c3062603afb3b2665856a548 /src/ui | |
parent | f5938745dcbe567d6e52f79b63151584d2c917d8 (diff) |
Text: Fixed a line wrapping issue
When the last safe break position was not in the current attributed run, the calculated wrap advance came out incorrect. This was possible when the first glyph in an attributed run didn't fit.
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/documentwidget.c | 6 | ||||
-rw-r--r-- | src/ui/text.c | 79 |
2 files changed, 50 insertions, 35 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 8fefb95c..924dc0c4 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -4522,8 +4522,10 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4522 | } | 4522 | } |
4523 | } | 4523 | } |
4524 | } | 4524 | } |
4525 | // drawRect_Paint(&d->paint, (iRect){ visPos, run->bounds.size }, green_ColorId); | 4525 | if (0) { |
4526 | // drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); | 4526 | drawRect_Paint(&d->paint, (iRect){ visPos, run->bounds.size }, green_ColorId); |
4527 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); | ||
4528 | } | ||
4527 | } | 4529 | } |
4528 | 4530 | ||
4529 | static int drawSideRect_(iPaint *p, iRect rect) { | 4531 | static int drawSideRect_(iPaint *p, iRect rect) { |
diff --git a/src/ui/text.c b/src/ui/text.c index 7afaf583..d22faea1 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -214,7 +214,6 @@ static void init_Font(iFont *d, const iFontSpec *fontSpec, const iFontFile *font | |||
214 | d->baseline = fontFile->ascent * d->yScale; | 214 | d->baseline = fontFile->ascent * d->yScale; |
215 | d->vertOffset = d->height * (1.0f - glyphScale) / 2 * fontSpec->vertOffsetScale[scaleType]; | 215 | d->vertOffset = d->height * (1.0f - glyphScale) / 2 * fontSpec->vertOffsetScale[scaleType]; |
216 | d->table = NULL; | 216 | d->table = NULL; |
217 | // printf("{%s} height:%d baseline:%d\n", cstr_String(&d->fontSpec->id), d->height, d->baseline); | ||
218 | } | 217 | } |
219 | 218 | ||
220 | static void deinit_Font(iFont *d) { | 219 | static void deinit_Font(iFont *d) { |
@@ -934,6 +933,12 @@ static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run, i | |||
934 | iAssert(endAt >= 0 && endAt <= size_Array(&d->logical)); | 933 | iAssert(endAt >= 0 && endAt <= size_Array(&d->logical)); |
935 | finishedRun.logical.end = endAt; | 934 | finishedRun.logical.end = endAt; |
936 | if (!isEmpty_Range(&finishedRun.logical)) { | 935 | if (!isEmpty_Range(&finishedRun.logical)) { |
936 | #if 0 | ||
937 | /* Colorize individual runs to see boundaries. */ | ||
938 | static int dbg; | ||
939 | static const int dbgClr[3] = { red_ColorId, green_ColorId, blue_ColorId }; | ||
940 | finishedRun.attrib.colorId = dbgClr[dbg++ % 3]; | ||
941 | #endif | ||
937 | pushBack_Array(&d->runs, &finishedRun); | 942 | pushBack_Array(&d->runs, &finishedRun); |
938 | run->flags.isLineBreak = iFalse; | 943 | run->flags.isLineBreak = iFalse; |
939 | run->flags.isArabic = iFalse; | 944 | run->flags.isArabic = iFalse; |
@@ -1120,15 +1125,18 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
1120 | continue; | 1125 | continue; |
1121 | } | 1126 | } |
1122 | if (ch == 0x20) { | 1127 | if (ch == 0x20) { |
1123 | if (run.font->fontSpec->flags & auxiliary_FontSpecFlag) { | 1128 | if (run.font->fontSpec->flags & auxiliary_FontSpecFlag && |
1129 | ~run.font->fontSpec->flags & allowSpacePunct_FontSpecFlag) { | ||
1124 | finishRun_AttributedText_(d, &run, pos); | 1130 | finishRun_AttributedText_(d, &run, pos); |
1125 | run.font = d->font; /* never use space from the symbols font, it's too wide */ | 1131 | run.font = d->font; /* auxilitary font space not allowed, could be wrong width */ |
1126 | } | 1132 | } |
1127 | continue; | 1133 | continue; |
1128 | } | 1134 | } |
1129 | iFont *currentFont = attribFont; | 1135 | iFont *currentFont = attribFont; |
1130 | if (run.font->fontSpec->flags & arabic_FontSpecFlag && isPunct_Char(ch)) { | 1136 | if (run.font->fontSpec->flags & auxiliary_FontSpecFlag && |
1131 | currentFont = run.font; /* remain as Arabic for whitespace */ | 1137 | run.font->fontSpec->flags & allowSpacePunct_FontSpecFlag && |
1138 | isPunct_Char(ch)) { | ||
1139 | currentFont = run.font; /* keep the current font */ | ||
1132 | } | 1140 | } |
1133 | const iGlyph *glyph = glyph_Font_(currentFont, ch); | 1141 | const iGlyph *glyph = glyph_Font_(currentFont, ch); |
1134 | if (index_Glyph_(glyph) && glyph->font != run.font) { | 1142 | if (index_Glyph_(glyph) && glyph->font != run.font) { |
@@ -1153,8 +1161,10 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
1153 | printf("[AttributedText] %zu runs:\n", size_Array(&d->runs)); | 1161 | printf("[AttributedText] %zu runs:\n", size_Array(&d->runs)); |
1154 | iConstForEach(Array, i, &d->runs) { | 1162 | iConstForEach(Array, i, &d->runs) { |
1155 | const iAttributedRun *run = i.value; | 1163 | const iAttributedRun *run = i.value; |
1156 | printf(" %zu %s log:%d...%d vis:%d...%d {%s}\n", index_ArrayConstIterator(&i), | 1164 | printf(" %zu %s fnt:%d log:%d...%d vis:%d...%d {%s}\n", |
1165 | index_ArrayConstIterator(&i), | ||
1157 | run->attrib.isRTL ? "<-" : "->", | 1166 | run->attrib.isRTL ? "<-" : "->", |
1167 | fontId_Text_(run->font), | ||
1158 | run->logical.start, run->logical.end - 1, | 1168 | run->logical.start, run->logical.end - 1, |
1159 | logToVis[run->logical.start], logToVis[run->logical.end - 1], | 1169 | logToVis[run->logical.start], logToVis[run->logical.end - 1], |
1160 | cstr_Rangecc(sourceRange_AttributedText_(d, run->logical))); | 1170 | cstr_Rangecc(sourceRange_AttributedText_(d, run->logical))); |
@@ -1598,6 +1608,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1598 | wrap->hitPoint.y < orig.y + yCursor + d->height); | 1608 | wrap->hitPoint.y < orig.y + yCursor + d->height); |
1599 | iBool wasCharHit = iFalse; /* on this line */ | 1609 | iBool wasCharHit = iFalse; /* on this line */ |
1600 | float breakAdvance = -1.0f; | 1610 | float breakAdvance = -1.0f; |
1611 | size_t breakRunIndex = iInvalidPos; | ||
1601 | iAssert(wrapPosRange.end == textLen); | 1612 | iAssert(wrapPosRange.end == textLen); |
1602 | /* Determine ends of wrapRuns and wrapVisRange. */ | 1613 | /* Determine ends of wrapRuns and wrapVisRange. */ |
1603 | int safeBreakPos = -1; | 1614 | int safeBreakPos = -1; |
@@ -1621,7 +1632,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1621 | iAssert(run->font == buf->font); | 1632 | iAssert(run->font == buf->font); |
1622 | shape_GlyphBuffer_(buf); | 1633 | shape_GlyphBuffer_(buf); |
1623 | iChar prevCh = 0; | 1634 | iChar prevCh = 0; |
1624 | lastAttrib = run->attrib; | 1635 | lastAttrib = run->attrib; |
1636 | // printf("checking run %zu...\n", runIndex); | ||
1625 | for (unsigned int ir = 0; ir < buf->glyphCount; ir++) { | 1637 | for (unsigned int ir = 0; ir < buf->glyphCount; ir++) { |
1626 | const int i = (run->attrib.isRTL ? buf->glyphCount - ir - 1 : ir); | 1638 | const int i = (run->attrib.isRTL ? buf->glyphCount - ir - 1 : ir); |
1627 | const hb_glyph_info_t *info = &buf->glyphInfo[i]; | 1639 | const hb_glyph_info_t *info = &buf->glyphInfo[i]; |
@@ -1648,24 +1660,27 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1648 | iAssert(xAdvance >= 0); | 1660 | iAssert(xAdvance >= 0); |
1649 | if (args->wrap->mode == word_WrapTextMode) { | 1661 | if (args->wrap->mode == word_WrapTextMode) { |
1650 | /* When word wrapping, only consider certain places breakable. */ | 1662 | /* When word wrapping, only consider certain places breakable. */ |
1651 | if (!isPunct_Char(ch) && (prevCh == '-' || prevCh == '/')) { | 1663 | if ((prevCh == '-' || prevCh == '/') && !isPunct_Char(ch)) { |
1652 | safeBreakPos = logPos; | 1664 | safeBreakPos = logPos; |
1653 | breakAdvance = wrapAdvance; | 1665 | breakAdvance = wrapAdvance; |
1654 | // printf("breakAdv_A:%f\n", breakAdvance); | 1666 | breakRunIndex = runIndex; |
1667 | // printf("sbp:%d breakAdv_A:%f\n", safeBreakPos, breakAdvance); | ||
1655 | // isSoftHyphenBreak = iFalse; | 1668 | // isSoftHyphenBreak = iFalse; |
1656 | } | 1669 | } |
1657 | else if (isSpace_Char(ch)) { | 1670 | else if (isSpace_Char(ch)) { |
1658 | safeBreakPos = logPos; | 1671 | safeBreakPos = logPos; |
1659 | breakAdvance = wrapAdvance; | 1672 | breakAdvance = wrapAdvance; |
1660 | // printf("breakAdv_B:%f sbb:%d\n", breakAdvance, safeBreakPos); | 1673 | breakRunIndex = runIndex; |
1674 | // printf("sbp:%d breakAdv_B:%f\n", safeBreakPos, breakAdvance); | ||
1661 | // isSoftHyphenBreak = iFalse; | 1675 | // isSoftHyphenBreak = iFalse; |
1662 | } | 1676 | } |
1663 | prevCh = ch; | 1677 | prevCh = ch; |
1664 | } | 1678 | } |
1665 | else { | 1679 | else { |
1666 | safeBreakPos = logPos; | 1680 | safeBreakPos = logPos; |
1667 | breakAdvance = wrapAdvance; | 1681 | breakAdvance = wrapAdvance; |
1668 | wrapAttrib = run->attrib; | 1682 | breakRunIndex = runIndex; |
1683 | wrapAttrib = run->attrib; | ||
1669 | } | 1684 | } |
1670 | if (isHitPointOnThisLine) { | 1685 | if (isHitPointOnThisLine) { |
1671 | if (wrap->hitPoint.x >= orig.x + wrapAdvance && | 1686 | if (wrap->hitPoint.x >= orig.x + wrapAdvance && |
@@ -1681,7 +1696,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1681 | if (wrap->maxWidth > 0 && | 1696 | if (wrap->maxWidth > 0 && |
1682 | wrapAdvance + xOffset + glyph->d[0].x + glyph->rect[0].size.x > | 1697 | wrapAdvance + xOffset + glyph->d[0].x + glyph->rect[0].size.x > |
1683 | args->wrap->maxWidth) { | 1698 | args->wrap->maxWidth) { |
1684 | // printf("safeBreakPos:%d\n", safeBreakPos); | 1699 | // printf("out of room at lp:%d! safeBreakPos:%d (idx:%zu) breakAdv:%f\n", |
1700 | // logPos, safeBreakPos, | ||
1701 | // breakRunIndex, breakAdvance); | ||
1685 | if (safeBreakPos >= 0) { | 1702 | if (safeBreakPos >= 0) { |
1686 | wrapPosRange.end = safeBreakPos; | 1703 | wrapPosRange.end = safeBreakPos; |
1687 | } | 1704 | } |
@@ -1697,6 +1714,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1697 | } | 1714 | } |
1698 | wrapPosRange.end = logPos; | 1715 | wrapPosRange.end = logPos; |
1699 | breakAdvance = wrapAdvance; | 1716 | breakAdvance = wrapAdvance; |
1717 | breakRunIndex = runIndex; | ||
1700 | } | 1718 | } |
1701 | wrapResumePos = wrapPosRange.end; | 1719 | wrapResumePos = wrapPosRange.end; |
1702 | if (args->wrap->mode != anyCharacter_WrapTextMode) { | 1720 | if (args->wrap->mode != anyCharacter_WrapTextMode) { |
@@ -1704,14 +1722,14 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1704 | wrapResumePos++; /* skip space */ | 1722 | wrapResumePos++; /* skip space */ |
1705 | } | 1723 | } |
1706 | } | 1724 | } |
1707 | wrapRuns.end = runIndex + 1; /* still includes this run */ | 1725 | wrapRuns.end = breakRunIndex + 1; /* still includes this run */ |
1708 | wrapResumeRunIndex = runIndex; /* ...but continue from the same one */ | 1726 | wrapResumeRunIndex = breakRunIndex; /* ...but continue from the same one */ |
1727 | // printf("-> wrapAdv:%f (breakAdv:%f)\n", wrapAdvance, breakAdvance); | ||
1709 | wrapAdvance = breakAdvance; | 1728 | wrapAdvance = breakAdvance; |
1710 | // printf("-> wrapAdv:%f (breakAdv)\n", wrapAdvance); | 1729 | // printf("wrapResumePos:%d\n", wrapResumePos); |
1711 | break; | 1730 | break; |
1712 | } | 1731 | } |
1713 | wrapAdvance += xAdvance; | 1732 | wrapAdvance += xAdvance; |
1714 | printf("lp:%d wrap:%f\n", logPos, wrapAdvance); | ||
1715 | /* Additional kerning tweak. It would be better to use HarfBuzz font callbacks, | 1733 | /* Additional kerning tweak. It would be better to use HarfBuzz font callbacks, |
1716 | but they don't seem to get called? */ | 1734 | but they don't seem to get called? */ |
1717 | if (i + 1 < buf->glyphCount) { | 1735 | if (i + 1 < buf->glyphCount) { |
@@ -1720,6 +1738,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1720 | buf->glyphInfo[i + 1].codepoint); | 1738 | buf->glyphInfo[i + 1].codepoint); |
1721 | } | 1739 | } |
1722 | } | 1740 | } |
1741 | // printf("...finished checking run %zu\n", runIndex); | ||
1723 | } | 1742 | } |
1724 | if (isHitPointOnThisLine && wrap->hitPoint.x >= orig.x + wrapAdvance) { | 1743 | if (isHitPointOnThisLine && wrap->hitPoint.x >= orig.x + wrapAdvance) { |
1725 | /* On the right side. */ | 1744 | /* On the right side. */ |
@@ -1774,16 +1793,17 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1774 | } | 1793 | } |
1775 | } | 1794 | } |
1776 | } | 1795 | } |
1777 | } | ||
1778 | #if 0 | 1796 | #if 0 |
1779 | printf("Run order: "); | 1797 | printf("Run order: "); |
1780 | iConstForEach(Array, ro, &runOrder) { | 1798 | iConstForEach(Array, ro, &runOrder) { |
1781 | const size_t *idx = ro.value; | 1799 | const size_t *idx = ro.value; |
1782 | printf("%zu {%s}\n", *idx, | 1800 | printf("%zu {%s}\n", *idx, |
1783 | cstr_Rangecc(sourceRange_AttributedText_(&attrText, ((const iAttributedRun *) at_Array(&attrText.runs, *idx))->logical))); | 1801 | cstr_Rangecc(sourceRange_AttributedText_(&attrText, ((const iAttributedRun *) at_Array(&attrText.runs, *idx))->logical))); |
1784 | } | 1802 | } |
1785 | printf("\n"); | 1803 | printf("\n"); |
1786 | #endif | 1804 | #endif |
1805 | |||
1806 | } | ||
1787 | iAssert(size_Array(&runOrder) == size_Range(&wrapRuns)); | 1807 | iAssert(size_Array(&runOrder) == size_Range(&wrapRuns)); |
1788 | /* Alignment. */ | 1808 | /* Alignment. */ |
1789 | int origin = 0; | 1809 | int origin = 0; |
@@ -1791,12 +1811,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1791 | if (isRightAligned) { | 1811 | if (isRightAligned) { |
1792 | if (layoutBound > 0) { | 1812 | if (layoutBound > 0) { |
1793 | origin = layoutBound - wrapAdvance; | 1813 | origin = layoutBound - wrapAdvance; |
1794 | printf("orig:%d (lbo:%d wrapAdv:%f)\n", origin, layoutBound, wrapAdvance); | ||
1795 | } | 1814 | } |
1796 | printf("yes; base RTL\n"); | ||
1797 | } | ||
1798 | else { | ||
1799 | printf("not base RTL\n"); | ||
1800 | } | 1815 | } |
1801 | /* Make a callback for each wrapped line. */ | 1816 | /* Make a callback for each wrapped line. */ |
1802 | if (wrap && wrap->wrapFunc && | 1817 | if (wrap && wrap->wrapFunc && |
@@ -1866,8 +1881,6 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1866 | const int bl1 = attrText.baseFont->baseline + attrText.baseFont->vertOffset; | 1881 | const int bl1 = attrText.baseFont->baseline + attrText.baseFont->vertOffset; |
1867 | const int bl2 = run->font->baseline + run->font->vertOffset; | 1882 | const int bl2 = run->font->baseline + run->font->vertOffset; |
1868 | dst.y += bl1 - bl2; | 1883 | dst.y += bl1 - bl2; |
1869 | // printf("baseline difference: run %d, base %d\n", | ||
1870 | // run->font->baseline, attrText.baseFont->baseline); | ||
1871 | } | 1884 | } |
1872 | if (mode & visualFlag_RunMode) { | 1885 | if (mode & visualFlag_RunMode) { |
1873 | if (isEmpty_Rect(bounds)) { | 1886 | if (isEmpty_Rect(bounds)) { |