[LyX/master] Use real italic slope for slanted caret

Jean-Marc Lasgouttes lasgouttes at lyx.org
Mon Sep 28 12:06:53 UTC 2020


commit e47092044b0c2e9c475d6d6c4269b2c511b0fed3
Author: Yuriy Skalko <yuriy.skalko at gmail.com>
Date:   Wed Sep 23 11:18:08 2020 +0300

    Use real italic slope for slanted caret
---
 src/frontends/FontMetrics.h         |    2 +
 src/frontends/qt/GuiFontMetrics.cpp |   28 +++++++++++++++++++++++
 src/frontends/qt/GuiFontMetrics.h   |    4 +++
 src/frontends/qt/GuiWorkArea.cpp    |   41 +++++++++++++++++++---------------
 4 files changed, 57 insertions(+), 18 deletions(-)

diff --git a/src/frontends/FontMetrics.h b/src/frontends/FontMetrics.h
index 0807a22..d6e6354 100644
--- a/src/frontends/FontMetrics.h
+++ b/src/frontends/FontMetrics.h
@@ -77,6 +77,8 @@ public:
 	virtual int strikeoutPos() const = 0;
 	/// return true if font is not upright (italic or oblique)
 	virtual bool italic() const = 0;
+	/// return slope for italic font
+	virtual double italicSlope() const = 0;
 
 	/// return the width of the char in the font
 	virtual int width(char_type c) const = 0;
diff --git a/src/frontends/qt/GuiFontMetrics.cpp b/src/frontends/qt/GuiFontMetrics.cpp
index ab8815d..77e04a1 100644
--- a/src/frontends/qt/GuiFontMetrics.cpp
+++ b/src/frontends/qt/GuiFontMetrics.cpp
@@ -20,11 +20,15 @@
 #include "support/convert.h"
 #include "support/lassert.h"
 #include "support/lyxlib.h"
+#include "support/debug.h"
 
 #define DISABLE_PMPROF
 #include "support/pmprof.h"
 
 #include <QByteArray>
+#include <QRawFont>
+#include <QtEndian>
+#include <QtMath>
 
 using namespace std;
 using namespace lyx::support;
@@ -113,6 +117,24 @@ GuiFontMetrics::GuiFontMetrics(QFont const & font)
 	  breakat_cache_(cache_metrics_breakat_size),
 	  qtextlayout_cache_(cache_metrics_qtextlayout_size)
 {
+	// Determine italic slope
+	double const defaultSlope = tan(qDegreesToRadians(19.0));
+	QRawFont raw = QRawFont::fromFont(font);
+	QByteArray post(raw.fontTable("post"));
+	if (post.length() == 0) {
+		slope_ = defaultSlope;
+		LYXERR(Debug::FONT, "Screen font doesn't have 'post' table.");
+	} else {
+		// post table description:
+		// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6post.html
+		int32_t italicAngle = qFromBigEndian(*reinterpret_cast<int32_t *>(post.data() + 4));
+		double angle = italicAngle / 65536.0; // Fixed-point 16.16 to floating-point
+		slope_ = -tan(qDegreesToRadians(angle));
+		// Correct italic fonts with zero slope
+		if (slope_ == 0.0 && font.italic())
+			slope_ = defaultSlope;
+		LYXERR(Debug::FONT, "Italic slope: " << slope_);
+	}
 }
 
 
@@ -167,6 +189,12 @@ bool GuiFontMetrics::italic() const
 }
 
 
+double GuiFontMetrics::italicSlope() const
+{
+	return slope_;
+}
+
+
 namespace {
 int const outOfLimitMetric = -10000;
 }
diff --git a/src/frontends/qt/GuiFontMetrics.h b/src/frontends/qt/GuiFontMetrics.h
index d5847a1..c19a3cd 100644
--- a/src/frontends/qt/GuiFontMetrics.h
+++ b/src/frontends/qt/GuiFontMetrics.h
@@ -43,6 +43,7 @@ public:
 	virtual int underlinePos() const;
 	virtual int strikeoutPos() const;
 	virtual bool italic() const;
+	virtual double italicSlope() const;
 	virtual int width(char_type c) const;
 	virtual int ascent(char_type c) const;
 	virtual int descent(char_type c) const;
@@ -85,6 +86,9 @@ private:
 	/// Metrics on the font
 	QFontMetrics metrics_;
 
+	/// Slope of italic font
+	double slope_;
+
 	/// Cache of char widths
 	mutable QHash<char_type, int> width_cache_;
 	/// Cache of string widths
diff --git a/src/frontends/qt/GuiWorkArea.cpp b/src/frontends/qt/GuiWorkArea.cpp
index 064d4cb..7e3b4fb 100644
--- a/src/frontends/qt/GuiWorkArea.cpp
+++ b/src/frontends/qt/GuiWorkArea.cpp
@@ -130,7 +130,7 @@ namespace frontend {
 class CaretWidget {
 public:
 	CaretWidget() : rtl_(false), l_shape_(false), completable_(false),
-		x_(0), caret_width_(0), slant_(false), ascent_(0)
+		x_(0), caret_width_(0), slant_(false), ascent_(0), slope_(0)
 	{}
 
 	/* Draw the caret. Parameter \c horiz_offset is not 0 when there
@@ -146,18 +146,18 @@ public:
 		int const lx = rtl_ ? x_ - rect_.left() : rect_.right() - x_;
 		int const bot = rect_.bottom();
 		int const dir = rtl_ ? -1 : 1;
-		// this is almost equal to tan(14 * PI / 180)
-		qreal const slope = 0.25;
 
 		// draw caret box
 		if (slant_ && !rtl_) {
-			// slanted (14 degree angle)
+			// slanted
+
 			QPainterPath path;
-			path.moveTo(x + ascent_ * slope, y);
-			path.lineTo(x - (rect_.height() - ascent_) * slope, y + rect_.height());
-			path.lineTo(x + dir * caret_width_ - (rect_.height() - ascent_) * slope,
+			path.moveTo(x + ascent_ * slope_, y);
+			path.lineTo(x - (rect_.height() - ascent_) * slope_,
+						y + rect_.height());
+			path.lineTo(x + dir * caret_width_ - (rect_.height() - ascent_) * slope_,
 			            y + rect_.height());
-			path.lineTo(x + dir * caret_width_ + ascent_ * slope, y);
+			path.lineTo(x + dir * caret_width_ + ascent_ * slope_, y);
 			painter.setRenderHint(QPainter::Antialiasing, true);
 			painter.fillPath(path, color_);
 			painter.setRenderHint(QPainter::Antialiasing, false);
@@ -176,7 +176,7 @@ public:
 			int const m = y + rect_.height() / 2;
 			int const d = TabIndicatorWidth - 1;
 			// offset for slanted carret
-			int const sx = (slant_ && !rtl_) ? (ascent_ - (rect_.height() / 2 - d)) * slope : 0;
+			int const sx = (slant_ && !rtl_) ? (ascent_ - (rect_.height() / 2 - d)) * slope_ : 0;
 			painter.drawLine(x + dir * (caret_width_ + 1) + sx, m - d,
 			                 x + dir * (caret_width_ + d + 1) + sx, m);
 			painter.drawLine(x + dir * (caret_width_ + 1) + sx, m + d,
@@ -185,7 +185,7 @@ public:
 	}
 
 	void update(int x, int y, int h, bool l_shape,
-		bool rtl, bool completable, bool slant, int ascent)
+		bool rtl, bool completable, bool slant, int ascent, double slope)
 	{
 		color_ = guiApp->colorCache().get(Color_cursor);
 		l_shape_ = l_shape;
@@ -194,6 +194,7 @@ public:
 		x_ = x;
 		slant_ = slant;
 		ascent_ = ascent;
+		slope_ = slope;
 
 		// extension to left and right
 		int l = 0;
@@ -245,6 +246,8 @@ private:
 	bool slant_;
 	/// the fontmetrics ascent for drawing slanted caret
 	int ascent_;
+	/// the slope for drawing slanted caret
+	double slope_;
 };
 
 
@@ -644,10 +647,11 @@ void GuiWorkArea::Private::updateCaretGeometry()
 	Point point;
 	int h = 0;
 	buffer_view_->caretPosAndHeight(point, h);
+	Cursor & cur = buffer_view_->cursor();
 
 	// RTL or not RTL
 	bool l_shape = false;
-	Font const & realfont = buffer_view_->cursor().real_current_font;
+	Font const & realfont = cur.real_current_font;
 	FontMetrics const & fm = theFontMetrics(realfont.fontInfo());
 	BufferParams const & bp = buffer_view_->buffer().params();
 	bool const samelang = realfont.language() == bp.language;
@@ -661,22 +665,23 @@ void GuiWorkArea::Private::updateCaretGeometry()
 		l_shape = false;
 
 	// show caret on screen
-	Cursor & cur = buffer_view_->cursor();
 	bool completable = cur.inset().showCompletionCursor()
 		&& completer_->completionAvailable()
 		&& !completer_->popupVisible()
 		&& !completer_->inlineVisible();
 
-	caret_->update(point.x_, point.y_, h, l_shape, isrtl, completable,
-		// use slanted caret for italics in text edit mode
-		fm.italic() && buffer_view_->cursor().inTexted()
-		// except for selections because the selection rect does not slant
-		&& !buffer_view_->cursor().selection(), fm.maxAscent());
+	// use slanted caret for italics in text edit mode
+	// except for selections because the selection rect does not slant
+	int slant = fm.italic() && buffer_view_->cursor().inTexted()
+		&& !buffer_view_->cursor().selection();
+	double slope = fm.italicSlope();
+
+	caret_->update(point.x_, point.y_, h, l_shape, isrtl, completable, slant,
+		fm.maxAscent(), slope);
 	needs_caret_geometry_update_ = false;
 }
 
 
-
 void GuiWorkArea::Private::showCaret()
 {
 	if (caret_visible_)


More information about the lyx-cvs mailing list