X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FGuiFontMetrics.cpp;h=76ebd907064e088a442cdd03fb2097196c1a2f8e;hb=425d092204118ea6c24c28e85fdf03fcf2bb51a4;hp=cf38d4d9c14bc0b6eeeed82c28560e1e411b3287;hpb=8c5f097b5d59cc157b39a08fa5d3f5fa82cf1e4f;p=lyx.git diff --git a/src/frontends/qt4/GuiFontMetrics.cpp b/src/frontends/qt4/GuiFontMetrics.cpp index cf38d4d9c1..76ebd90706 100644 --- a/src/frontends/qt4/GuiFontMetrics.cpp +++ b/src/frontends/qt4/GuiFontMetrics.cpp @@ -15,16 +15,39 @@ #include "qt_helpers.h" -#include "language.h" -#include "dimension.h" +#include "Language.h" +#include "Dimension.h" -#include "support/unicode.h" +#include "insets/Inset.h" -using std::string; +#include "support/lassert.h" + +using namespace std; namespace lyx { namespace frontend { +/** + * Convert a UCS4 character into a QChar. + * This is a hack (it does only make sense for the common part of the UCS4 + * and UTF16 encodings) and should not be used. + * This does only exist because of performance reasons (a real conversion + * using iconv is too slow on windows). + */ +static inline QChar const ucs4_to_qchar(char_type const ucs4) +{ + LASSERT(is_utf16(ucs4), /**/); + return QChar(static_cast(ucs4)); +} + + +// Caution: When using ucs4_to_qchar() in these methods, this is no +// real conversion but a simple cast in reality. This is the reason +// why this works well for symbol fonts used in mathed too, even though +// these are not real ucs4 characters. These are codepoints in the +// modern fonts used, nothing unicode related. +// See comment in QLPainter::text() for more explanation. + GuiFontMetrics::GuiFontMetrics(QFont const & font) : metrics_(font), smallcaps_metrics_(font), smallcaps_shape_(false) { @@ -53,59 +76,84 @@ int GuiFontMetrics::maxDescent() const int GuiFontMetrics::lbearing(char_type c) const { + if (!is_utf16(c)) + // FIXME: QFontMetrics::leftBearingdoes not support the + // full unicode range. Once it does, we could use: + //return metrics_.leftBearing(toqstr(docstring(1, c))); + return 0; + return metrics_.leftBearing(ucs4_to_qchar(c)); } +namespace { +int const outOfLimitMetric = -10000; +} + + int GuiFontMetrics::rbearing(char_type c) const { - if (!rbearing_cache_.contains(c)) { - // Qt rbearing is from the right edge of the char's width(). + int value = rbearing_cache_.value(c, outOfLimitMetric); + if (value != outOfLimitMetric) + return value; + + // Qt rbearing is from the right edge of the char's width(). + if (is_utf16(c)) { QChar sc = ucs4_to_qchar(c); - int rb = metrics_.width(sc) - metrics_.rightBearing(sc); - rbearing_cache_.insert(c, rb); + value = width(c) - metrics_.rightBearing(sc); + } else { + // FIXME: QFontMetrics::leftBearing does not support the + // full unicode range. Once it does, we could use: + // metrics_.rightBearing(toqstr(docstring(1, c))); + value = width(c); } - return rbearing_cache_.value(c); + + rbearing_cache_.insert(c, value); + + return value; } -int GuiFontMetrics::smallcapsWidth(QString const & s) const +int GuiFontMetrics::smallcapsWidth(char_type c) const { - int w = 0; - int const ls = s.size(); - - for (int i = 0; i < ls; ++i) { - QChar const & c = s[i]; - QChar const uc = c.toUpper(); - if (c != uc) - w += smallcaps_metrics_.width(uc); + // FIXME: Optimisation probably needed: we don't use the width cache. + if (is_utf16(c)) { + QChar const qc = ucs4_to_qchar(c); + QChar const uc = qc.toUpper(); + if (qc != uc) + return smallcaps_metrics_.width(uc); else - w += metrics_.width(c); + return metrics_.width(qc); + } else { + QString const s = toqstr(docstring(1, c)); + QString const us = s.toUpper(); + if (s != us) + return smallcaps_metrics_.width(us); + else + return metrics_.width(s); } - return w; } int GuiFontMetrics::width(docstring const & s) const { size_t ls = s.size(); - if (ls == 0) - return 0; - - if (ls == 1 && !smallcaps_shape_) { - return width(s[0]); - } - - if (smallcaps_shape_) - // Caution: The following ucs4 to QString conversions work - // for symbol fonts only because they are no real conversions - // but simple casts in reality. See comment in QLPainter::text() - // for more explanation. - return smallcapsWidth(toqstr(s)); - int w = 0; - for (unsigned int i = 0; i < ls; ++i) + for (unsigned int i = 0; i < ls; ++i) { + //FIXME: we need to detect surrogate pairs and act accordingly + /** + if isSurrogateBase(s[i]) { + docstring c = s[i]; + if (smallcaps_shape_) + w += metrics_.width(toqstr(c + s[i + 1])); + else + w += smallcaps_metrics_.width(toqstr(c + s[i + 1])); + ++i; + } + else + */ w += width(s[i]); + } return w; } @@ -113,19 +161,7 @@ int GuiFontMetrics::width(docstring const & s) const int GuiFontMetrics::width(QString const & ucs2) const { - int const ls = ucs2.size(); - if (ls == 1 && !smallcaps_shape_) { - return width(ucs2[0].unicode()); - } - - if (smallcaps_shape_) - return smallcapsWidth(ucs2); - - int w = 0; - for (int i = 0; i < ls; ++i) - w += width(ucs2[i].unicode()); - - return w; + return width(qstring_to_ucs4(ucs2)); } @@ -144,8 +180,9 @@ int GuiFontMetrics::signedWidth(docstring const & s) const void GuiFontMetrics::rectText(docstring const & str, int & w, int & ascent, int & descent) const { - static int const d = 2; - w = width(str) + d * 2 + 2; + static int const d = Inset::TEXT_TO_INSET_OFFSET / 2; + + w = width(str) + Inset::TEXT_TO_INSET_OFFSET; ascent = metrics_.ascent() + d; descent = metrics_.descent() + d; } @@ -155,10 +192,8 @@ void GuiFontMetrics::rectText(docstring const & str, void GuiFontMetrics::buttonText(docstring const & str, int & w, int & ascent, int & descent) const { - static int const d = 3; - w = width(str) + d * 2 + 2; - ascent = metrics_.ascent() + d; - descent = metrics_.descent() + d; + rectText(str, w, ascent, descent); + w += Inset::TEXT_TO_INSET_OFFSET; } @@ -174,45 +209,68 @@ Dimension const GuiFontMetrics::dimension(char_type c) const } -void GuiFontMetrics::fillMetricsCache(char_type c) const +GuiFontMetrics::AscendDescend const GuiFontMetrics::fillMetricsCache( + char_type c) const { - QRect const & r = metrics_.boundingRect(ucs4_to_qchar(c)); + QRect r; + if (is_utf16(c)) + r = metrics_.boundingRect(ucs4_to_qchar(c)); + else + r = metrics_.boundingRect(toqstr(docstring(1, c))); + AscendDescend ad = { -r.top(), r.bottom() + 1}; // We could as well compute the width but this is not really // needed for now as it is done directly in width() below. metrics_cache_.insert(c, ad); + + return ad; } int GuiFontMetrics::width(char_type c) const { if (smallcaps_shape_) - return smallcapsWidth(ucs4_to_qchar(c)); + return smallcapsWidth(c); - if (!width_cache_.contains(c)) { - width_cache_.insert(c, metrics_.width(ucs4_to_qchar(c))); - } + int value = width_cache_.value(c, outOfLimitMetric); + if (value != outOfLimitMetric) + return value; - return width_cache_.value(c); + if (is_utf16(c)) + value = metrics_.width(ucs4_to_qchar(c)); + else + value = metrics_.width(toqstr(docstring(1, c))); + + width_cache_.insert(c, value); + + return value; } int GuiFontMetrics::ascent(char_type c) const { - if (!metrics_cache_.contains(c)) - fillMetricsCache(c); - - return metrics_cache_.value(c).ascent; + static AscendDescend const outOfLimitAD = + {outOfLimitMetric, outOfLimitMetric}; + AscendDescend value = metrics_cache_.value(c, outOfLimitAD); + if (value.ascent != outOfLimitMetric) + return value.ascent; + + value = fillMetricsCache(c); + return value.ascent; } int GuiFontMetrics::descent(char_type c) const { - if (!metrics_cache_.contains(c)) - fillMetricsCache(c); - - return metrics_cache_.value(c).descent; + static AscendDescend const outOfLimitAD = + {outOfLimitMetric, outOfLimitMetric}; + AscendDescend value = metrics_cache_.value(c, outOfLimitAD); + if (value.descent != outOfLimitMetric) + return value.descent; + + value = fillMetricsCache(c); + return value.descent; } -} // frontend -} // lyx +} // namespace frontend +} // namespace lyx