commit b9e45e4196f63c1b079aeb29422ce264bc4a01db
Author: Jean-Marc Lasgouttes <[email protected]>
Date: Fri Sep 1 15:42:00 2023 +0200
Improve computation of text vertical dimension
Some fonts possess glyphs which ascents/descents are larger that the font
global ascent/descent. This is known to happen on macOS.
This patch introduces code to make these individual ascents/descents
available:
1/ FontMetrics::breakString now provides a full dimension for each line
of text that it produces
2/ The new FontMetrics::dimension provides the dimension of a given string
3/ FontMetrics::rectText is modified to rely on this new method.
The new code is used when constructing rows to replace some of the
uses of FontMetrics::maxAscent/Descent by proper values. This holds in
particular for calculation of row height.
Possible fix to bug #12879.
---
src/Row.cpp | 8 ++++----
src/TextMetrics.cpp | 12 ++++++-----
src/frontends/FontMetrics.h | 10 ++++++---
src/frontends/qt/GuiFontMetrics.cpp | 41 ++++++++++++++++++++++++++++++-------
src/frontends/qt/GuiFontMetrics.h | 3 +++
5 files changed, 55 insertions(+), 19 deletions(-)
diff --git a/src/Row.cpp b/src/Row.cpp
index 563868cf36..474b134c9b 100644
--- a/src/Row.cpp
+++ b/src/Row.cpp
@@ -142,7 +142,7 @@ bool Row::Element::splitAt(int const width, int next_width,
SplitType split_type
if (!(row_flags & CanBreakInside)) {
// has width been computed yet?
if (dim.wid == 0)
- dim.wid = fm.width(str);
+ dim = fm.dimension(str);
return false;
}
@@ -156,7 +156,7 @@ bool Row::Element::splitAt(int const width, int next_width,
SplitType split_type
if ((split_type == FIT && breaks.front().nspc_wid > width)
|| (breaks.size() > 1 && breaks.front().len == 0)) {
if (dim.wid == 0)
- dim.wid = fm.width(str);
+ dim = fm.dimension(str);
return false;
}
@@ -170,7 +170,7 @@ bool Row::Element::splitAt(int const width, int next_width,
SplitType split_type
for (FontMetrics::Break const & brk : breaks) {
Element e(type, curpos, font, change);
e.str = str.substr(i, brk.len);
- e.dim.wid = brk.wid;
+ e.dim = brk.dim;
e.nspc_wid = brk.nspc_wid;
e.row_flags = CanBreakInside | BreakAfter;
if (type == PREEDIT) {
@@ -554,7 +554,7 @@ void Row::addVirtual(pos_type const pos, docstring const &
s,
finalizeLast();
Element e(VIRTUAL, pos, f, ch);
e.str = s;
- e.dim.wid = theFontMetrics(f).width(s);
+ e.dim = theFontMetrics(f).dimension(s);
e.endpos = pos;
// Copy after* flags from previous elements, forbid break before element
int const prev_row_flags = elements_.empty() ? Inline :
elements_.back().row_flags;
diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp
index ce2af92a12..46e2b9c9df 100644
--- a/src/TextMetrics.cpp
+++ b/src/TextMetrics.cpp
@@ -1415,10 +1415,11 @@ void TextMetrics::setRowHeight(Row & row) const
if (row.pos() == 0 && layout.labelIsInline()) {
FontInfo const labelfont = text_->labelFont(par);
FontMetrics const & lfm = theFontMetrics(labelfont);
- maxasc = max(maxasc, int(lfm.maxAscent()
+ Dimension const ldim = lfm.dimension(par.labelString());
+ maxasc = max(maxasc, int(ldim.ascent()
// add leading space
+ lfm.maxHeight() * (spacing_val - 1)));
- maxdes = max(maxdes, int(lfm.maxDescent()));
+ maxdes = max(maxdes, int(ldim.descent()));
}
// Find the ascent/descent of the row contents
@@ -1428,10 +1429,11 @@ void TextMetrics::setRowHeight(Row & row) const
maxdes = max(maxdes, e.dim.descent());
} else {
FontMetrics const & fm2 = theFontMetrics(e.font);
- maxasc = max(maxasc, int(fm2.maxAscent()
+ maxasc = max(maxasc, e.dim.ascent()
+ // FIXME: is this right (or shall we use a real
height) ??
// add leading space
- + fm2.maxHeight() * (spacing_val - 1)));
- maxdes = max(maxdes, int(fm2.maxDescent()));
+ + int(fm2.maxHeight() * (spacing_val - 1)));
+ maxdes = max(maxdes, e.dim.descent());
}
}
diff --git a/src/frontends/FontMetrics.h b/src/frontends/FontMetrics.h
index 28e3dec4dd..e8700931ba 100644
--- a/src/frontends/FontMetrics.h
+++ b/src/frontends/FontMetrics.h
@@ -14,6 +14,7 @@
#ifndef FONT_METRICS_H
#define FONT_METRICS_H
+#include "Dimension.h"
#include "support/mute_warning.h"
#include "support/docstring.h"
@@ -128,11 +129,11 @@ public:
// The places where to break a string and the width of the resulting
lines.
struct Break {
- Break(int l, int w, int nsw) : len(l), wid(w), nspc_wid(nsw) {}
+ Break(int l, Dimension const & d, int nsw) : len(l), dim(d),
nspc_wid(nsw) {}
// Number of characters
int len = 0;
- // text width
- int wid = 0;
+ // text dimension
+ Dimension dim;
// text width when trailing spaces are removed; only makes a
// difference for the last break.
int nspc_wid = 0;
@@ -151,6 +152,9 @@ public:
virtual Breaks
breakString(docstring const & s, int first_wid, int wid, bool rtl, bool
force) const = 0;
+ // return string dimension for the font.
+ virtual Dimension const dimension(docstring const & str) const = 0;
+
/// return char dimension for the font.
virtual Dimension const dimension(char_type c) const = 0;
/**
diff --git a/src/frontends/qt/GuiFontMetrics.cpp
b/src/frontends/qt/GuiFontMetrics.cpp
index f0b100101e..3766a6379f 100644
--- a/src/frontends/qt/GuiFontMetrics.cpp
+++ b/src/frontends/qt/GuiFontMetrics.cpp
@@ -75,6 +75,8 @@ namespace {
// Limit strwidth_cache_ total cost to 1MB of string data.
int const strwidth_cache_max_cost = 1024 * 1024;
+// Limit strdim_cache_ total cost to 1MB of string data.
+int const strdim_cache_max_cost = 1024 * 1024;
// Limit breakstr_cache_ total cost to 10MB of string data.
// This is useful for documents with very large insets.
int const breakstr_cache_max_cost = 10 * 1024 * 1024;
@@ -114,6 +116,7 @@ inline QChar const ucs4_to_qchar(char_type const ucs4)
GuiFontMetrics::GuiFontMetrics(QFont const & font)
: font_(font), metrics_(font, 0),
xheight_(-metrics_.boundingRect('x').top()),
strwidth_cache_(strwidth_cache_max_cost),
+ strdim_cache_(strdim_cache_max_cost),
breakstr_cache_(breakstr_cache_max_cost),
qtextlayout_cache_(qtextlayout_cache_max_size)
{
@@ -566,15 +569,18 @@ GuiFontMetrics::breakString_helper(docstring const & s,
int first_wid, int wid,
QTextLine const & line = tl.lineAt(i);
int const line_epos = line.textStart() + line.textLength();
int const epos = tlh.qpos2pos(line_epos);
+ Dimension dim;
// This does not take trailing spaces into account, except for
the last line.
- int const wid = iround(line.naturalTextWidth());
+ dim.wid = iround(line.naturalTextWidth());
+ dim.asc = iround(line.ascent());
+ dim.des = iround(line.descent());
// If the line is not the last one, trailing space is always
omitted.
- int nspc_wid = wid;
+ int nspc_wid = dim.wid;
// For the last line, compute the width without trailing space
if (i + 1 == tl.lineCount() && !s.empty() && isSpace(s.back())
&& line.textStart() <= tlh.pos2qpos(s.size() - 1))
nspc_wid = iround(line.cursorToX(tlh.pos2qpos(s.size()
- 1)));
- breaks.emplace_back(epos - pos, wid, nspc_wid);
+ breaks.emplace_back(epos - pos, dim, nspc_wid);
pos = epos;
}
@@ -615,10 +621,10 @@ void GuiFontMetrics::rectText(docstring const & str,
{
// FIXME: let offset depend on font (this is Inset::TEXT_TO_OFFSET)
int const offset = 4;
-
- w = width(str) + offset;
- ascent = metrics_.ascent() + offset / 2;
- descent = metrics_.descent() + offset / 2;
+ Dimension const dim = dimension(str);
+ w = dim.wid + offset;
+ ascent = dim.asc + offset / 2;
+ descent = dim.des + offset / 2;
}
@@ -642,6 +648,27 @@ Dimension const GuiFontMetrics::dimension(char_type c)
const
}
+Dimension const GuiFontMetrics::dimension(docstring const & s) const
+{
+ PROFILE_THIS_BLOCK(dimension);
+ if (Dimension * dim_p = strdim_cache_.object_ptr(s))
+ return *dim_p;
+ PROFILE_CACHE_MISS(dimension);
+ QTextLayout tl;
+ tl.setText(toqstr(s));
+ tl.setFont(font_);
+ tl.beginLayout();
+ QTextLine line = tl.createLine();
+ tl.endLayout();
+ Dimension dim;
+ dim.wid = iround(line.naturalTextWidth());
+ dim.asc = iround(line.ascent());
+ dim.des = iround(line.descent());
+ strdim_cache_.insert(s, dim, s.size() * sizeof(char_type));
+ return dim;
+}
+
+
GuiFontMetrics::AscendDescend const GuiFontMetrics::fillMetricsCache(
char_type c) const
{
diff --git a/src/frontends/qt/GuiFontMetrics.h
b/src/frontends/qt/GuiFontMetrics.h
index 5f73172c0d..24a1c801d0 100644
--- a/src/frontends/qt/GuiFontMetrics.h
+++ b/src/frontends/qt/GuiFontMetrics.h
@@ -83,6 +83,7 @@ public:
int x2pos(docstring const & s, int & x, bool rtl, double ws) const
override;
Breaks breakString(docstring const & s, int first_wid, int wid, bool
rtl, bool force) const override;
Dimension const dimension(char_type c) const override;
+ Dimension const dimension(docstring const & s) const override;
void rectText(docstring const & str,
int & width,
@@ -126,6 +127,8 @@ private:
mutable QHash<char_type, int> width_cache_;
/// Cache of string widths
mutable Cache<docstring, int> strwidth_cache_;
+ /// Cache of string dimension
+ mutable Cache<docstring, Dimension> strdim_cache_;
/// Cache for breakString
mutable Cache<BreakStringKey, Breaks> breakstr_cache_;
/// Cache for QTextLayout
--
lyx-cvs mailing list
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-cvs