Work around issues with Qt5 and Arabic text

This fixes two particular problems

* with Qt5, it seems that QFontMetrics::width does not return the
  correct value for some Arabic text; this patch uses QTextLayout
  instead to compute a string width

* Likewise, the undocumented layout flags TextForceRightToLeft and
  TextForceLeftToRight do not work with Arabic text; this patch uses
  unicode override characters instead.

It might be that the two issues are related. In any case, they do not
happen with latin text where right-to-left direction is enforced. And
they do not happen with Qt4.

Additionally, remove some dead code in GuiFontMetrics::pos2x().

Fixes bug #10436.

(cherry picked from commit e832d2e90f)
This commit is contained in:
Jean-Marc Lasgouttes 2016-10-23 20:52:01 +02:00
parent 10b8421ed6
commit 24648404b3

View File

@ -146,7 +146,16 @@ int GuiFontMetrics::width(docstring const & s) const
int * pw = strwidth_cache_[qba]; int * pw = strwidth_cache_[qba];
if (pw) if (pw)
return *pw; return *pw;
int w = metrics_.width(toqstr(s)); // For some reason QMetrics::width returns a wrong value with Qt5
// int w = metrics_.width(toqstr(s));
QTextLayout tl;
tl.setText(toqstr(s));
tl.setFont(font_);
tl.beginLayout();
QTextLine line = tl.createLine();
tl.endLayout();
int w = int(line.naturalTextWidth());
strwidth_cache_.insert(qba, new int(w), qba.size()); strwidth_cache_.insert(qba, new int(w), qba.size());
return w; return w;
} }
@ -196,8 +205,6 @@ GuiFontMetrics::getTextLayout(docstring const & s, QFont font,
int GuiFontMetrics::pos2x(docstring const & s, int const pos, bool const rtl, int GuiFontMetrics::pos2x(docstring const & s, int const pos, bool const rtl,
double const wordspacing) const double const wordspacing) const
{ {
QFont copy = font_;
copy.setWordSpacing(wordspacing);
QTextLayout const & tl = getTextLayout(s, font_, rtl, wordspacing); QTextLayout const & tl = getTextLayout(s, font_, rtl, wordspacing);
return static_cast<int>(tl.lineForTextPosition(pos).cursorToX(pos)); return static_cast<int>(tl.lineForTextPosition(pos).cursorToX(pos));
} }
@ -228,10 +235,27 @@ bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const
*/ */
// Unicode character ZERO WIDTH NO-BREAK SPACE // Unicode character ZERO WIDTH NO-BREAK SPACE
QChar const zerow_nbsp(0xfeff); QChar const zerow_nbsp(0xfeff);
tl.setText(zerow_nbsp + toqstr(s) + zerow_nbsp); QString str = zerow_nbsp + toqstr(s) + zerow_nbsp;
tl.setFont(font_); #if 1
/* Use unicode override characters to enforce drawing direction
* Source: http://www.iamcal.com/understanding-bidirectional-text/
*/
if (rtl)
// Right-to-left override: forces to draw text right-to-left
str = QChar(0x202E) + str;
else
// Left-to-right override: forces to draw text left-to-right
str = QChar(0x202D) + str;
int const offset = 2;
#else
// Alternative version that breaks with Qt5 and arabic text (#10436)
// Note that both setFlags and the enums are undocumented // Note that both setFlags and the enums are undocumented
tl.setFlags(rtl ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight); tl.setFlags(rtl ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight);
int const offset = 1;
#endif
tl.setText(str);
tl.setFont(font_);
QTextOption to; QTextOption to;
to.setWrapMode(force ? QTextOption::WrapAnywhere : QTextOption::WordWrap); to.setWrapMode(force ? QTextOption::WrapAnywhere : QTextOption::WordWrap);
tl.setTextOption(to); tl.setTextOption(to);
@ -240,11 +264,11 @@ bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const
line.setLineWidth(x); line.setLineWidth(x);
tl.createLine(); tl.createLine();
tl.endLayout(); tl.endLayout();
if ((force && line.textLength() == 1) || int(line.naturalTextWidth()) > x) if ((force && line.textLength() == offset) || int(line.naturalTextWidth()) > x)
return false; return false;
x = int(line.naturalTextWidth()); x = int(line.naturalTextWidth());
// The -1 is here to account for the leading zerow_nbsp. // The offset is here to account for the extra leading characters.
s = s.substr(0, line.textLength() - 1); s = s.substr(0, line.textLength() - offset);
return true; return true;
} }