Cleanup of spacing in mathed

This is a first cleanup step. More complex rules have to be
implemented on top of this.

Use proper spacing \thinmuskip, \medmuskip and \thickmuskip instead of
ad-hoc values.

Rename isRelOp to isMathRel and introduce isMathBin and isMathPunct
(for InsetMathChar and InsetMathSymbol). Update the categories of
characters in InsetMathChar according to LaTeX source (fontmath.ltx).

Set correctly the spacing around mathrel, mathbin and mathpunct
elements. Use \thinmuskip around MathDelim instead of a hardcoded 4.

This is related to bug #8883.
This commit is contained in:
Jean-Marc Lasgouttes 2016-05-24 12:08:24 +02:00 committed by Richard Heck
parent 320b616c50
commit 65a6cc1fc3
9 changed files with 125 additions and 38 deletions

View File

@ -162,8 +162,12 @@ public:
/// identifies things that can get scripts /// identifies things that can get scripts
virtual bool isScriptable() const { return false; } virtual bool isScriptable() const { return false; }
/// is the a relational operator (used for splitting equations) /// identifies a binary operators (used for spacing)
virtual bool isRelOp() const { return false; } virtual bool isMathBin() const { return false; }
/// identifies relational operators (used for spacing and splitting equations)
virtual bool isMathRel() const { return false; }
/// identifies punctuation (used for spacing)
virtual bool isMathPunct() const { return false; }
/// will this get written as a single block in {..} /// will this get written as a single block in {..}
virtual bool extraBraces() const { return false; } virtual bool extraBraces() const { return false; }

View File

@ -26,7 +26,6 @@
#include "support/debug.h" #include "support/debug.h"
#include "support/lstrings.h" #include "support/lstrings.h"
#include "support/lyxlib.h"
#include "support/textutils.h" #include "support/textutils.h"
@ -35,12 +34,6 @@ namespace lyx {
extern bool has_math_fonts; extern bool has_math_fonts;
static bool isBinaryOp(char_type c)
{
return support::contains("+-<>=/*", static_cast<char>(c));
}
static bool slanted(char_type c) static bool slanted(char_type c)
{ {
return isAlphaASCII(c) || Encodings::isMathAlpha(c); return isAlphaASCII(c) || Encodings::isMathAlpha(c);
@ -76,11 +69,15 @@ void InsetMathChar::metrics(MetricsInfo & mi, Dimension & dim) const
dim = fm.dimension(char_); dim = fm.dimension(char_);
kerning_ = fm.rbearing(char_) - dim.wid; kerning_ = fm.rbearing(char_) - dim.wid;
} }
int const em = mathed_font_em(mi.base.font); if (isMathBin())
if (isBinaryOp(char_)) dim.wid += 2 * mathed_medmuskip(mi.base.font);
dim.wid += support::iround(0.5 * em); else if (isMathRel())
dim.wid += 2 * mathed_thickmuskip(mi.base.font);
else if (isMathPunct())
dim.wid += mathed_thinmuskip(mi.base.font);
else if (char_ == '\'') else if (char_ == '\'')
dim.wid += support::iround(0.1667 * em); // FIXME: don't know where this is coming from
dim.wid += mathed_thinmuskip(mi.base.font);
#else #else
whichFont(font_, code_, mi); whichFont(font_, code_, mi);
dim = theFontMetrics(font_).dimension(char_); dim = theFontMetrics(font_).dimension(char_);
@ -94,11 +91,12 @@ void InsetMathChar::metrics(MetricsInfo & mi, Dimension & dim) const
void InsetMathChar::draw(PainterInfo & pi, int x, int y) const void InsetMathChar::draw(PainterInfo & pi, int x, int y) const
{ {
//lyxerr << "drawing '" << char_ << "' font: " << pi.base.fontname << std::endl; //lyxerr << "drawing '" << char_ << "' font: " << pi.base.fontname << std::endl;
int const em = mathed_font_em(pi.base.font); if (isMathBin())
if (isBinaryOp(char_)) x += mathed_medmuskip(pi.base.font);
x += support::iround(0.25 * em); else if (isMathRel())
x += mathed_thickmuskip(pi.base.font);
else if (char_ == '\'') else if (char_ == '\'')
x += support::iround(0.0833 * em); x += mathed_thinmuskip(pi.base.font) / 2;
#if 1 #if 1
if (char_ == '=' && has_math_fonts) { if (char_ == '=' && has_math_fonts) {
FontSetChanger dummy(pi.base, "cmr"); FontSetChanger dummy(pi.base, "cmr");
@ -237,9 +235,21 @@ void InsetMathChar::htmlize(HtmlStream & ms) const
} }
bool InsetMathChar::isRelOp() const bool InsetMathChar::isMathBin() const
{ {
return char_ == '=' || char_ == '<' || char_ == '>'; return support::contains("+-*", static_cast<char>(char_));
}
bool InsetMathChar::isMathRel() const
{
return support::contains("<>=:", static_cast<char>(char_));
}
bool InsetMathChar::isMathPunct() const
{
return support::contains(",;", static_cast<char>(char_));
} }

View File

@ -49,7 +49,11 @@ public:
/// ///
char_type getChar() const { return char_; } char_type getChar() const { return char_; }
/// ///
bool isRelOp() const; bool isMathBin() const;
///
bool isMathRel() const;
///
bool isMathPunct() const;
/// ///
InsetCode lyxCode() const { return MATH_CHAR_CODE; } InsetCode lyxCode() const { return MATH_CHAR_CODE; }

View File

@ -115,7 +115,7 @@ void InsetMathDelim::metrics(MetricsInfo & mi, Dimension & dim) const
dw_ = 8; dw_ = 8;
if (dw_ < 4) if (dw_ < 4)
dw_ = 4; dw_ = 4;
dim.wid = dim0.width() + 2 * dw_ + 8; dim.wid = dim0.width() + 2 * dw_ + 2 * mathed_thinmuskip(mi.base.font);
dim.asc = max(a0, d0) + h0; dim.asc = max(a0, d0) + h0;
dim.des = max(a0, d0) - h0; dim.des = max(a0, d0) - h0;
} }
@ -125,9 +125,10 @@ void InsetMathDelim::draw(PainterInfo & pi, int x, int y) const
{ {
Dimension const dim = dimension(*pi.base.bv); Dimension const dim = dimension(*pi.base.bv);
int const b = y - dim.asc; int const b = y - dim.asc;
cell(0).draw(pi, x + dw_ + 4, y); int const skip = mathed_thinmuskip(pi.base.font);
mathed_draw_deco(pi, x + 4, b, dw_, dim.height(), left_); cell(0).draw(pi, x + dw_ + skip, y);
mathed_draw_deco(pi, x + dim.width() - dw_ - 4, mathed_draw_deco(pi, x + skip, b, dw_, dim.height(), left_);
mathed_draw_deco(pi, x + dim.width() - dw_ - skip,
b, dw_, dim.height(), right_); b, dw_, dim.height(), right_);
setPosCache(pi, x, y); setPosCache(pi, x, y);
} }

View File

@ -106,7 +106,7 @@ namespace {
size_t firstRelOp(MathData const & ar) size_t firstRelOp(MathData const & ar)
{ {
for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it) for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
if ((*it)->isRelOp()) if ((*it)->isMathRel())
return it - ar.begin(); return it - ar.begin();
return ar.size(); return ar.size();
} }

View File

@ -21,7 +21,6 @@
#include "support/debug.h" #include "support/debug.h"
#include "support/docstream.h" #include "support/docstream.h"
#include "support/lyxlib.h"
#include "support/textutils.h" #include "support/textutils.h"
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
@ -68,7 +67,6 @@ void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const
sym_->extra == "mathalpha" && sym_->extra == "mathalpha" &&
mi.base.fontname == "mathit"; mi.base.fontname == "mathit";
std::string const font = italic_upcase_greek ? "cmm" : sym_->inset; std::string const font = italic_upcase_greek ? "cmm" : sym_->inset;
int const em = mathed_font_em(mi.base.font);
FontSetChanger dummy(mi.base, from_ascii(font)); FontSetChanger dummy(mi.base, from_ascii(font));
mathed_string_dim(mi.base.font, sym_->draw, dim); mathed_string_dim(mi.base.font, sym_->draw, dim);
docstring::const_reverse_iterator rit = sym_->draw.rbegin(); docstring::const_reverse_iterator rit = sym_->draw.rbegin();
@ -80,10 +78,15 @@ void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const
dim.des -= h_; dim.des -= h_;
} }
// seperate things a bit // seperate things a bit
if (isRelOp()) if (isMathBin())
dim.wid += support::iround(0.5 * em); dim.wid += 2 * mathed_medmuskip(mi.base.font);
else else if (isMathRel())
dim.wid += support::iround(0.1667 * em); dim.wid += 2 * mathed_thickmuskip(mi.base.font);
else if (isMathPunct())
dim.wid += mathed_thinmuskip(mi.base.font);
// FIXME: I see no reason for this
//else
// dim.wid += support::iround(0.1667 * em);
scriptable_ = false; scriptable_ = false;
if (mi.base.style == LM_ST_DISPLAY) if (mi.base.style == LM_ST_DISPLAY)
@ -105,11 +108,13 @@ void InsetMathSymbol::draw(PainterInfo & pi, int x, int y) const
sym_->extra == "mathalpha" && sym_->extra == "mathalpha" &&
pi.base.fontname == "mathit"; pi.base.fontname == "mathit";
std::string const font = italic_upcase_greek ? "cmm" : sym_->inset; std::string const font = italic_upcase_greek ? "cmm" : sym_->inset;
int const em = mathed_font_em(pi.base.font); if (isMathBin())
if (isRelOp()) x += mathed_medmuskip(pi.base.font);
x += support::iround(0.25 * em); else if (isMathRel())
else x += mathed_thickmuskip(pi.base.font);
x += support::iround(0.0833 * em); // FIXME: I see no reason for this
//else
// x += support::iround(0.0833 * em);
FontSetChanger dummy(pi.base, from_ascii(font)); FontSetChanger dummy(pi.base, from_ascii(font));
pi.draw(x, y - h_, sym_->draw); pi.draw(x, y - h_, sym_->draw);
@ -122,12 +127,24 @@ InsetMath::mode_type InsetMathSymbol::currentMode() const
} }
bool InsetMathSymbol::isRelOp() const bool InsetMathSymbol::isMathBin() const
{
return sym_->extra == "mathbin";
}
bool InsetMathSymbol::isMathRel() const
{ {
return sym_->extra == "mathrel"; return sym_->extra == "mathrel";
} }
bool InsetMathSymbol::isMathPunct() const
{
return sym_->extra == "mathpunct";
}
bool InsetMathSymbol::isOrdAlpha() const bool InsetMathSymbol::isOrdAlpha() const
{ {
return sym_->extra == "mathord" || sym_->extra == "mathalpha"; return sym_->extra == "mathord" || sym_->extra == "mathalpha";

View File

@ -40,7 +40,11 @@ public:
/// ///
mode_type currentMode() const; mode_type currentMode() const;
/// ///
bool isRelOp() const; bool isMathRel() const;
///
bool isMathBin() const;
///
bool isMathPunct() const;
/// ///
bool isOrdAlpha() const; bool isOrdAlpha() const;
/// do we take scripts? /// do we take scripts?

View File

@ -25,6 +25,7 @@
#include "support/debug.h" #include "support/debug.h"
#include "support/docstream.h" #include "support/docstream.h"
#include "support/lyxlib.h"
#include <map> #include <map>
#include <algorithm> #include <algorithm>
@ -506,6 +507,46 @@ int mathed_font_em(FontInfo const & font)
return theFontMetrics(font).em(); return theFontMetrics(font).em();
} }
/* The math units. Quoting TeX by Topic, p.205:
*
* Spacing around mathematical objects is measured in mu units. A mu
* is 1/18th part of \fontdimen6 of the font in family 2 in the
* current style, the quad value of the symbol font.
*
* A \thickmuskip (default value in plain TeX: 5mu plus 5mu) is
* inserted around (binary) relations, except where these are preceded
* or followed by other relations or punctuation, and except if they
* follow an open, or precede a close symbol.
*
* A \medmuskip (default value in plain TeX: 4mu plus 2mu minus 4mu)
* is put around binary operators.
*
* A \thinmuskip (default value in plain TeX: 3mu) follows after
* punctuation, and is put around inner objects, except where these
* are followed by a close or preceded by an open symbol, and except
* if the other object is a large operator or a binary relation.
*/
int mathed_thinmuskip(FontInfo font)
{
font.setFamily(SYMBOL_FAMILY);
return support::iround(3.0 / 18 * theFontMetrics(font).em());
}
int mathed_medmuskip(FontInfo font)
{
font.setFamily(SYMBOL_FAMILY);
return support::iround(4.0 / 18 * theFontMetrics(font).em());
}
int mathed_thickmuskip(FontInfo font)
{
font.setFamily(SYMBOL_FAMILY);
return support::iround(5.0 / 18 * theFontMetrics(font).em());
}
int mathed_char_width(FontInfo const & font, char_type c) int mathed_char_width(FontInfo const & font, char_type c)
{ {

View File

@ -29,6 +29,12 @@ class InsetMath;
int mathed_font_em(FontInfo const &); int mathed_font_em(FontInfo const &);
int mathed_thinmuskip(FontInfo font);
int mathed_medmuskip(FontInfo font);
int mathed_thickmuskip(FontInfo font);
int mathed_char_width(FontInfo const &, char_type c); int mathed_char_width(FontInfo const &, char_type c);
int mathed_char_kerning(FontInfo const &, char_type c); int mathed_char_kerning(FontInfo const &, char_type c);