]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt/GuiFontMetrics.cpp
Change FontMetrics::breakAt to return a position
[lyx.git] / src / frontends / qt / GuiFontMetrics.cpp
index 9ff027eb089b8a1e68b70827b2497818452a6e0e..25bf7a4ff7be287085b477e7dab7f0ecdf22fac6 100644 (file)
 #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>
+
+#if QT_VERSION >= 0x050100
+#include <QtMath>
+#else
+#define qDegreesToRadians(degree) (degree * (M_PI / 180))
+#endif
 
 using namespace std;
 using namespace lyx::support;
@@ -48,6 +57,15 @@ using namespace lyx::support;
 #  error "Define at least one of BIDI_USE_OVERRIDE or BIDI_USE_FLAG"
 #endif
 
+
+#if QT_VERSION < 0x050000
+inline uint qHash(double key)
+{
+       return qHash(QByteArray(reinterpret_cast<char const *>(&key), sizeof(key)));
+}
+#endif
+
+
 namespace std {
 
 /*
@@ -113,6 +131,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_);
+       }
 }
 
 
@@ -126,6 +162,7 @@ int GuiFontMetrics::maxDescent() const
 {
        // We add 1 as the value returned by QT is different than X
        // See http://doc.trolltech.com/2.3/qfontmetrics.html#200b74
+       // FIXME: check this
        return metrics_.descent() + 1;
 }
 
@@ -161,6 +198,18 @@ int GuiFontMetrics::strikeoutPos() const
 }
 
 
+bool GuiFontMetrics::italic() const
+{
+       return font_.italic();
+}
+
+
+double GuiFontMetrics::italicSlope() const
+{
+       return slope_;
+}
+
+
 namespace {
 int const outOfLimitMetric = -10000;
 }
@@ -213,8 +262,8 @@ int GuiFontMetrics::rbearing(char_type c) const
 int GuiFontMetrics::width(docstring const & s) const
 {
        PROFILE_THIS_BLOCK(width);
-       if (strwidth_cache_.contains(s))
-               return strwidth_cache_[s];
+       if (int * wid_p = strwidth_cache_.object_ptr(s))
+               return *wid_p;
        PROFILE_CACHE_MISS(width);
        /* Several problems have to be taken into account:
         * * QFontMetrics::width does not returns a wrong value with Qt5 with
@@ -245,18 +294,25 @@ int GuiFontMetrics::width(docstring const & s) const
 #else
        bool const math_char = s.length() == 1;
 #endif
-       // keep value 0 for math chars with width 0
-       if (!math_char || metrics_.width(toqstr(s)) != 0) {
+       if (math_char) {
+               QString const qs = toqstr(s);
+               int br_width = metrics_.boundingRect(qs).width();
+#if QT_VERSION >= 0x050b00
+               int s_width = metrics_.horizontalAdvance(qs);
+#else
+               int s_width = metrics_.width(qs);
+#endif
+               // keep value 0 for math chars with width 0
+               if (s_width != 0)
+                       w = max(br_width, s_width);
+       } else {
                QTextLayout tl;
                tl.setText(toqstr(s));
                tl.setFont(font_);
                tl.beginLayout();
                QTextLine line = tl.createLine();
                tl.endLayout();
-               if (math_char)
-                       w = iround(line.naturalTextWidth());
-               else
-                       w = iround(line.horizontalAdvance());
+               w = iround(line.horizontalAdvance());
        }
        strwidth_cache_.insert(s, w, s.size() * sizeof(char_type));
        return w;
@@ -281,14 +337,19 @@ int GuiFontMetrics::signedWidth(docstring const & s) const
 }
 
 
+uint qHash(TextLayoutKey const & key)
+{
+       double params = (2 * key.rtl - 1) * key.ws;
+       return std::qHash(key.s) ^ ::qHash(params);
+}
+
 shared_ptr<QTextLayout const>
 GuiFontMetrics::getTextLayout(docstring const & s, bool const rtl,
                               double const wordspacing) const
 {
        PROFILE_THIS_BLOCK(getTextLayout);
-       docstring const s_cache =
-               s + (rtl ? "r" : "l") + convert<docstring>(wordspacing);
-       if (auto ptl = qtextlayout_cache_[s_cache])
+       TextLayoutKey key{s, rtl, wordspacing};
+       if (auto ptl = qtextlayout_cache_[key])
                return ptl;
        PROFILE_CACHE_MISS(getTextLayout);
        auto const ptl = make_shared<QTextLayout>();
@@ -321,7 +382,7 @@ GuiFontMetrics::getTextLayout(docstring const & s, bool const rtl,
        ptl->beginLayout();
        ptl->createLine();
        ptl->endLayout();
-       qtextlayout_cache_.insert(s_cache, ptl);
+       qtextlayout_cache_.insert(key, ptl);
        return ptl;
 }
 
@@ -472,7 +533,7 @@ GuiFontMetrics::breakAt_helper(docstring const & s, int const x,
        tl.endLayout();
        int const line_wid = iround(line.horizontalAdvance());
        if ((force && line.textLength() == offset) || line_wid > x)
-               return {-1, -1};
+               return {-1, line_wid};
        /* Since QString is UTF-16 and docstring is UCS-4, the offsets may
         * not be the same when there are high-plan unicode characters
         * (bug #10443).
@@ -496,32 +557,37 @@ GuiFontMetrics::breakAt_helper(docstring const & s, int const x,
                --len;
        LASSERT(len > 0 || qlen == 0, /**/);
 #endif
+       // si la chaîne est déjà trop courte, on ne coupe pas
+       if (len == static_cast<int>(s.length()))
+               len = -1;
        return {len, line_wid};
 }
 
 
-bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const force) const
+uint qHash(BreakAtKey const & key)
+{
+       int params = key.force + 2 * key.rtl + 4 * key.x;
+       return std::qHash(key.s) ^ ::qHash(params);
+}
+
+
+int GuiFontMetrics::breakAt(docstring const & s, int & x, bool const rtl, bool const force) const
 {
        PROFILE_THIS_BLOCK(breakAt);
        if (s.empty())
                return false;
 
-       docstring const s_cache =
-               s + convert<docstring>(x) + (rtl ? "r" : "l") + (force ? "f" : "w");
+       BreakAtKey key{s, x, rtl, force};
        pair<int, int> pp;
-
-       if (breakat_cache_.contains(s_cache))
-               pp = breakat_cache_[s_cache];
+       if (auto * pp_ptr = breakat_cache_.object_ptr(key))
+               pp = *pp_ptr;
        else {
                PROFILE_CACHE_MISS(breakAt);
                pp = breakAt_helper(s, x, rtl, force);
-               breakat_cache_.insert(s_cache, pp, s_cache.size() * sizeof(char_type));
+               breakat_cache_.insert(key, pp, sizeof(key) + s.size() * sizeof(char_type));
        }
-       if (pp.first == -1)
-               return false;
-       s = s.substr(0, pp.first);
        x = pp.second;
-       return true;
+       return pp.first;
 }
 
 
@@ -581,10 +647,17 @@ int GuiFontMetrics::width(char_type c) const
        if (value != outOfLimitMetric)
                return value;
 
+#if QT_VERSION >= 0x050b00
+       if (is_utf16(c))
+               value = metrics_.horizontalAdvance(ucs4_to_qchar(c));
+       else
+               value = metrics_.horizontalAdvance(toqstr(docstring(1, c)));
+#else
        if (is_utf16(c))
                value = metrics_.width(ucs4_to_qchar(c));
        else
                value = metrics_.width(toqstr(docstring(1, c)));
+#endif
 
        width_cache_.insert(c, value);