Implement rule 17

* Convert the kerning into a proper right margin for mathnormal, mathscr and
  mathcal (fonts with \fontdimen2 == 0 as per rule 17 from TeXBook).

* Simulate the fact that characters in mathnormal fonts have a 0-width left
  bearing.

* Implement subscript positioning in the case of rule 17 using negative italic
  correction (kerning_).
This commit is contained in:
Guillaume Munch 2016-11-27 22:45:51 +01:00
parent 566d008c9d
commit 03a4b8c932
8 changed files with 47 additions and 18 deletions

View File

@ -230,7 +230,7 @@ public:
/// math stuff usually isn't allowed in text mode
virtual bool allowedIn(mode_type mode) const { return mode == MATH_MODE; }
/// superscript kerning
/// Italic correction as described in InsetMathScript.h
virtual int kerning(BufferView const *) const { return 0; }
///
bool isInToc() const { return true; }

View File

@ -29,6 +29,10 @@
#include "support/lstrings.h"
#include "support/textutils.h"
#include <algorithm>
using namespace std;
namespace lyx {
@ -105,20 +109,38 @@ Inset * InsetMathChar::clone() const
void InsetMathChar::metrics(MetricsInfo & mi, Dimension & dim) const
{
bool const mathfont = isMathFont(mi.base.fontname);
if (mathfont && subst_) {
string const & f = mi.base.fontname;
if (isMathFont(f) && subst_) {
// If the char has a substitute, draw the replacement symbol
// instead, but only in math mode.
mathedSymbolDim(mi.base, dim, subst_);
kerning_ = mathed_char_kerning(mi.base.font, *subst_->draw.rbegin());
return;
} else if (!slanted(char_) && mi.base.fontname == "mathnormal") {
} else if (!slanted(char_) && f == "mathnormal") {
Changer dummy = mi.base.font.changeShape(UP_SHAPE);
dim = theFontMetrics(mi.base.font).dimension(char_);
kerning_ = 0;
} else {
frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
dim = fm.dimension(char_);
kerning_ = fm.rbearing(char_) - dim.wid;
kerning_ = mathed_char_kerning(mi.base.font, char_);
// cmmi has almost no left bearing: simulate this
if (f == "mathnormal") {
dim.wid += max(-fm.lbearing(char_), 0);
}
// Rule 17 from Appendix G
// These are the fonts with fontdimen(2)==0.
// To properly handle the case fontdimen(2)!=0 (that is for all other
// math fonts), where the italic correction must also be converted into
// kerning but only at the end of strings of characters with the same
// font, one would need a more elaborate implementation in MathRow. For
// now the case fontdimen(2)==0 is the most important.
if (f == "mathnormal" || f == "mathscr" || f == "mathcal") {
dim.wid += kerning_;
// We use a negative value to tell InsetMathScript to move the
// subscript leftwards instead of the superscript rightwards
kerning_ = -kerning_;
}
}
}
@ -138,6 +160,10 @@ void InsetMathChar::draw(PainterInfo & pi, int x, int y) const
return;
}
}
// cmmi has almost no left bearing: simulate this
if (pi.base.fontname == "mathnormal") {
x += max(-theFontMetrics(pi.base.font).lbearing(char_), 0);
}
pi.draw(x, y, char_);
}

View File

@ -226,7 +226,8 @@ int InsetMathScript::dx0(BufferView const & bv) const
{
LASSERT(hasDown(), return 0);
Dimension const dim = dimension(bv);
return hasLimits() ? (dim.wid - down().dimension(bv).width()) / 2 : nwid(bv);
return hasLimits() ? (dim.wid - down().dimension(bv).width()) / 2
: nwid(bv) + min(nker(&bv), 0);
}
@ -234,7 +235,8 @@ int InsetMathScript::dx1(BufferView const & bv) const
{
LASSERT(hasUp(), return 0);
Dimension const dim = dimension(bv);
return hasLimits() ? (dim.wid - up().dimension(bv).width()) / 2 : nwid(bv) + nker(&bv);
return hasLimits() ? (dim.wid - up().dimension(bv).width()) / 2
: nwid(bv) + max(nker(&bv), 0);
}
@ -265,10 +267,8 @@ int InsetMathScript::ndes(BufferView const & bv) const
int InsetMathScript::nker(BufferView const * bv) const
{
if (!nuc().empty()) {
int kerning = nuc().kerning(bv);
return kerning > 0 ? kerning : 0;
}
if (!nuc().empty())
return nuc().kerning(bv);
return 0;
}
@ -320,9 +320,9 @@ void InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const
dim.wid = max(dim.wid, dimdown.width());
} else {
if (hasUp())
dim.wid = max(dim.wid, nker(mi.base.bv) + dimup.width());
dim.wid = max(dim.wid, max(nker(mi.base.bv), 0) + dimup.width());
if (hasDown())
dim.wid = max(dim.wid, dimdown.width());
dim.wid = max(dim.wid, min(nker(mi.base.bv), 0) + dimdown.width());
dim.wid += nwid(bv);
}
int na = nasc(bv);

View File

@ -135,7 +135,10 @@ private:
int nasc(BufferView const &) const;
/// returns descent of nucleus if any
int ndes(BufferView const &) const;
/// returns superscript kerning of nucleus if any
/// Italic correction: amount of displacement between subscript and
/// superscript in math mode as per Appendix G, rule 18f. A positive value
/// shifts the superscript to the right, and a negative value shifts the
/// subscript to the left.
int nker(BufferView const * bv) const;
/// where do we have to draw the scripts?
bool hasLimits() const;

View File

@ -104,7 +104,7 @@ private:
int nasc(BufferView const &) const;
/// returns descent of nucleus if any
int ndes(BufferView const &) const;
/// returns subscript and superscript kerning of nucleus if any
/// Italic correction as described in InsetMathScript.h
int nker(BufferView const * bv) const;
/// Whether there are two left scripts or one single cell
bool scriptl_;

View File

@ -61,7 +61,7 @@ void InsetMathSpecialChar::metrics(MetricsInfo & mi, Dimension & dim) const
} else {
frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
dim = fm.dimension(char_);
kerning_ = fm.rbearing(char_) - dim.wid;
kerning_ = mathed_char_kerning(mi.base.font, char_);
}
}

View File

@ -166,7 +166,7 @@ public:
int slevel() const { return slevel_; }
/// additional super/subscript shift
int sshift() const { return sshift_; }
/// superscript kerning
/// Italic correction as described in InsetMathScript.h
int kerning(BufferView const *) const { return kerning_; }
///
void swap(MathData & ar) { base_type::swap(ar); }

View File

@ -556,7 +556,7 @@ int mathed_char_width(FontInfo const & font, char_type c)
int mathed_char_kerning(FontInfo const & font, char_type c)
{
frontend::FontMetrics const & fm = theFontMetrics(font);
return fm.rbearing(c) - fm.width(c);
return max(0, fm.rbearing(c) - fm.width(c));
}