lyx_mirror/src/mathed/InsetMathChar.cpp
Jean-Marc Lasgouttes 361bd53bc3 Introduce the notion of math class
This done according to the TeXbook. This class replaces the individual
isMathXXX() methods. The mathClass() method (currently unused) is
provided for the following insets:

 * InsetMathChar (with a revised list of affected characters)
 * InsetMathSymbol: the class is given by the `extra' field
   Operators defined in lib/symbols (e.g. \log) are MC_OP
 * InsetMathFrac is MC_INNER (except nicefrac and units)
 * InsetDelimiters is MC_INNER
 * InsetStackrel is MC_REL
 * The class of InsetScript is the class of the last element of its
   nucleus (yes, it is a hack, but doing it right is more work).

Remove the explicit spacing that was done in the different insets. The spacing
will be reintroduced properly in a forthcoming commit.
2016-11-16 15:21:52 +01:00

284 lines
6.6 KiB
C++

/**
* \file InsetMathChar.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Alejandro Aguilar Sierra
* \author André Pönitz
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "InsetMathChar.h"
#include "MathParser.h"
#include "MathSupport.h"
#include "MathStream.h"
#include "MetricsInfo.h"
#include "Dimension.h"
#include "BufferEncodings.h"
#include "LaTeXFeatures.h"
#include "TextPainter.h"
#include "frontends/FontMetrics.h"
#include "support/debug.h"
#include "support/lstrings.h"
#include "support/textutils.h"
namespace lyx {
extern bool has_math_fonts;
namespace {
latexkeys const * makeSubstitute(char_type c)
{
std::string name;
switch (c) {
// Latex replaces ', *, -, and : with specific symbols. With unicode-math,
// these symbols are replaced respectively by ^U+2032, U+2217, U+2212 and
// U+2236 (the latter substitution can be turned off with a package
// option). Unicode-math also replaces ` with \backprime.
// prime needs to be placed in superscript unless an opentype font is used.
//case '\'':
//name = "prime";
//break;
case '*':
name = "ast";
break;
case '-':
name = "lyxminus";// unicode-math: "minus"
break;
case ':':
name = "ordinarycolon";// unicode-math: "mathratio"
break;
// The remaining replacements are not real character substitutions (from a
// unicode point of view) but are done here: 1. for cosmetic reasons, in the
// context of being stuck with CM fonts at the moment, to ensure consistency
// with related symbols: -, \leq, \geq, etc. 2. to get the proper spacing
// as defined in lib/symbols.
case '+':
name = "lyxplus";//unicode-math: "mathplus"
break;
case '>':
name = "lyxgt";//unicode-math: "greater"
break;
case '<':
name = "lyxlt";//unicode-math: "less"
break;
case '=':
name = "lyxeqrel";//unicode-math: "equal"
break;
//case ','://unicode-math: "mathcomma"
//case ';'://unicode-math: "mathsemicolon"
default:
return nullptr;
}
return in_word_set(from_ascii(name));
}
} //anonymous namespace
static bool slanted(char_type c)
{
return isAlphaASCII(c) || Encodings::isMathAlpha(c);
}
InsetMathChar::InsetMathChar(char_type c)
: char_(c), kerning_(0), subst_(makeSubstitute(c))
{}
Inset * InsetMathChar::clone() const
{
return new InsetMathChar(*this);
}
void InsetMathChar::metrics(MetricsInfo & mi, Dimension & dim) const
{
bool const mathfont = isMathFont(mi.base.fontname);
if (mathfont && subst_) {
// If the char has a substitute, draw the replacement symbol
// instead, but only in math mode.
mathedSymbolDim(mi, dim, subst_);
kerning_ = mathed_char_kerning(mi.base.font, *subst_->draw.rbegin());
return;
} else if (!slanted(char_) && mi.base.fontname == "mathnormal") {
Changer dummy = mi.base.font.changeShape(UP_SHAPE);
dim = theFontMetrics(mi.base.font).dimension(char_);
} else {
frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
dim = fm.dimension(char_);
kerning_ = fm.rbearing(char_) - dim.wid;
}
}
void InsetMathChar::draw(PainterInfo & pi, int x, int y) const
{
//lyxerr << "drawing '" << char_ << "' font: " << pi.base.fontname << std::endl;
if (isMathFont(pi.base.fontname)) {
if (subst_) {
// If the char has a substitute, draw the replacement symbol
// instead, but only in math mode.
mathedSymbolDraw(pi, x, y, subst_);
return;
} else if (!slanted(char_) && pi.base.fontname == "mathnormal") {
Changer dummy = pi.base.font.changeShape(UP_SHAPE);
pi.draw(x, y, char_);
return;
}
}
pi.draw(x, y, char_);
}
void InsetMathChar::metricsT(TextMetricsInfo const &, Dimension & dim) const
{
dim.wid = 1;
dim.asc = 1;
dim.des = 0;
}
void InsetMathChar::drawT(TextPainter & pain, int x, int y) const
{
//lyxerr << "drawing text '" << char_ << "' code: " << code_ << endl;
pain.draw(x, y, char_);
}
void InsetMathChar::write(WriteStream & os) const
{
os.os().put(char_);
}
void InsetMathChar::validate(LaTeXFeatures & features) const
{
if (!isASCII(char_))
BufferEncodings::validate(char_, features, true);
}
void InsetMathChar::normalize(NormalStream & os) const
{
os << "[char ";
os.os().put(char_);
os << " mathalpha]";
}
void InsetMathChar::octave(OctaveStream & os) const
{
os.os().put(char_);
}
// We have a bit of a problem here. MathML wants to know whether the
// character represents an "identifier" or an "operator", and we have
// no general way of telling. So we shall guess: If it's alpha or
// mathalpha, then we'll treat it as an identifier, otherwise as an
// operator.
// Worst case: We get bad spacing, or bad italics.
void InsetMathChar::mathmlize(MathStream & ms) const
{
std::string entity;
switch (char_) {
case '<': entity = "&lt;"; break;
case '>': entity = "&gt;"; break;
case '&': entity = "&amp;"; break;
case ' ': {
ms << from_ascii("&nbsp;");
return;
}
default: break;
}
if (ms.inText()) {
if (entity.empty())
ms.os().put(char_);
else
ms << from_ascii(entity);
return;
}
if (!entity.empty()) {
ms << "<mo>" << from_ascii(entity) << "</mo>";
return;
}
char const * type =
(isAlphaASCII(char_) || Encodings::isMathAlpha(char_))
? "mi" : "mo";
// we don't use MTag and ETag because we do not want the spacing
ms << "<" << type << ">" << char_type(char_) << "</" << type << ">";
}
void InsetMathChar::htmlize(HtmlStream & ms) const
{
std::string entity;
// Not taking subst_ into account here because the MathML output of
// <>=+-* looks correct as it is. FIXME: ' is not output as ^\prime
switch (char_) {
case '<': entity = "&lt;"; break;
case '>': entity = "&gt;"; break;
case '&': entity = "&amp;"; break;
case ' ': entity = "&nbsp;"; break;
default: break;
}
bool have_entity = !entity.empty();
if (ms.inText()) {
if (have_entity)
ms << from_ascii(entity);
else
ms.os().put(char_);
return;
}
if (have_entity) {
// an operator, so give some space
ms << ' ' << from_ascii(entity) << ' ';
return;
}
if (isAlphaASCII(char_) || Encodings::isMathAlpha(char_))
// we don't use MTag and ETag because we do not want the spacing
ms << MTag("i") << char_type(char_) << ETag("i");
else
// an operator, so give some space
ms << " " << char_type(char_) << " ";
}
MathClass InsetMathChar::mathClass() const
{
// this information comes from fontmath.ltx in LaTeX source.
char const ch = static_cast<char>(char_);
if (subst_)
return string_to_class(subst_->extra);
else if (support::contains(",;", ch))
return MC_PUNCT;
else if (support::contains("([", ch))
return MC_OPEN;
else if (support::contains(")]!?", ch))
return MC_CLOSE;
else return MC_ORD;
}
} // namespace lyx