The following patch posted to
   http://www.lyx.org/trac/ticket/10436
works around two probable Qt5 bugs related to Arabic text:

* with Qt5, QFontMetrics::width does not return the correct value for some Arabic text

 * Likewise, the undocumented layout flags TextForceRightToLeft and
TextForceLeftToRight do not work properly with Arabic text when trying to restrict the width of QTextLine. Again the width of text is not computed properly.

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.

It would be nice to report at least the first issue (see GuiFontMetrics::width), but I do not know how to make a self contained Qt program for this kind of thing. How minimal can I afford to be?

While the second issue is about an undocumented flag, it is probably worth reporting too.

I'd also be happy to have reports that the patch works in your favorite weird configuration.

JMarc


>From 0a66b79ab82f033ec1afa44a11bb57fad83151d3 Mon Sep 17 00:00:00 2001
From: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date: Sun, 23 Oct 2016 20:52:01 +0200
Subject: [PATCH] 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 dead code in GuiFontMetrics::pos2x().

Fixes bug #10436.
---
 src/frontends/qt4/GuiFontMetrics.cpp | 41 +++++++++++++++++++++++++++++-------
 1 file changed, 33 insertions(+), 8 deletions(-)

diff --git a/src/frontends/qt4/GuiFontMetrics.cpp b/src/frontends/qt4/GuiFontMetrics.cpp
index eade8cc..27dda7c 100644
--- a/src/frontends/qt4/GuiFontMetrics.cpp
+++ b/src/frontends/qt4/GuiFontMetrics.cpp
@@ -146,7 +146,17 @@ int GuiFontMetrics::width(docstring const & s) const
 	int * pw = strwidth_cache_[qba];
 	if (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());
+
+	lyxerr << "WIDTH[" << s << "]=" << w <<endl;
 	strwidth_cache_.insert(qba, new int(w), qba.size());
 	return w;
 }
@@ -196,8 +206,6 @@ GuiFontMetrics::getTextLayout(docstring const & s, QFont font,
 int GuiFontMetrics::pos2x(docstring const & s, int const pos, bool const rtl,
                           double const wordspacing) const
 {
-	QFont copy = font_;
-	copy.setWordSpacing(wordspacing);
 	QTextLayout const & tl = getTextLayout(s, font_, rtl, wordspacing);
 	return static_cast<int>(tl.lineForTextPosition(pos).cursorToX(pos));
 }
@@ -228,10 +236,27 @@ bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const
 	*/
 	// Unicode character ZERO WIDTH NO-BREAK SPACE
 	QChar const zerow_nbsp(0xfeff);
-	tl.setText(zerow_nbsp + toqstr(s) + zerow_nbsp);
-	tl.setFont(font_);
+	QString str = zerow_nbsp + toqstr(s) + zerow_nbsp;
+#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
 	tl.setFlags(rtl ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight);
+	int const offset = 1;
+#endif
+
+	tl.setText(str);
+	tl.setFont(font_);
 	QTextOption to;
 	to.setWrapMode(force ? QTextOption::WrapAnywhere : QTextOption::WordWrap);
 	tl.setTextOption(to);
@@ -240,11 +265,11 @@ bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const
 	line.setLineWidth(x);
 	tl.createLine();
 	tl.endLayout();
-	if ((force && line.textLength() == 1) || int(line.naturalTextWidth()) > x)
+	if ((force && line.textLength() == offset) || int(line.naturalTextWidth()) > x)
 		return false;
 	x = int(line.naturalTextWidth());
-	// The -1 is here to account for the leading zerow_nbsp.
-	s = s.substr(0, line.textLength() - 1);
+	// The offset is here to account for the extra leading characters.
+	s = s.substr(0, line.textLength() - offset);
 	return true;
 }
 
-- 
2.9.3

Reply via email to