RenderMathMLToken.cpp [plain text]
#include "config.h"
#include "RenderMathMLToken.h"
#if ENABLE(MATHML)
#include "MathMLElement.h"
#include "MathMLNames.h"
#include "MathMLTokenElement.h"
#include "PaintInfo.h"
#include "RenderElement.h"
#include "RenderIterator.h"
#include <wtf/IsoMallocInlines.h>
namespace WebCore {
using namespace MathMLNames;
WTF_MAKE_ISO_ALLOCATED_IMPL(RenderMathMLToken);
RenderMathMLToken::RenderMathMLToken(MathMLTokenElement& element, RenderStyle&& style)
: RenderMathMLBlock(element, WTFMove(style))
{
}
RenderMathMLToken::RenderMathMLToken(Document& document, RenderStyle&& style)
: RenderMathMLBlock(document, WTFMove(style))
{
}
MathMLTokenElement& RenderMathMLToken::element()
{
return static_cast<MathMLTokenElement&>(nodeForNonAnonymous());
}
void RenderMathMLToken::updateTokenContent()
{
RenderMathMLBlock::updateFromElement();
setMathVariantGlyphDirty();
}
struct MathVariantMapping {
uint32_t key;
uint32_t replacement;
};
static inline UChar32 ExtractKey(const MathVariantMapping* entry) { return entry->key; }
static UChar32 MathVariantMappingSearch(uint32_t key, const MathVariantMapping* table, size_t tableLength)
{
if (const auto* entry = tryBinarySearch<const MathVariantMapping, UChar32>(table, tableLength, key, ExtractKey))
return entry->replacement;
return 0;
}
static const MathVariantMapping arabicInitialMapTable[] = {
{ 0x628, 0x1EE21 },
{ 0x62A, 0x1EE35 },
{ 0x62B, 0x1EE36 },
{ 0x62C, 0x1EE22 },
{ 0x62D, 0x1EE27 },
{ 0x62E, 0x1EE37 },
{ 0x633, 0x1EE2E },
{ 0x634, 0x1EE34 },
{ 0x635, 0x1EE31 },
{ 0x636, 0x1EE39 },
{ 0x639, 0x1EE2F },
{ 0x63A, 0x1EE3B },
{ 0x641, 0x1EE30 },
{ 0x642, 0x1EE32 },
{ 0x643, 0x1EE2A },
{ 0x644, 0x1EE2B },
{ 0x645, 0x1EE2C },
{ 0x646, 0x1EE2D },
{ 0x647, 0x1EE24 },
{ 0x64A, 0x1EE29 }
};
static const MathVariantMapping arabicTailedMapTable[] = {
{ 0x62C, 0x1EE42 },
{ 0x62D, 0x1EE47 },
{ 0x62E, 0x1EE57 },
{ 0x633, 0x1EE4E },
{ 0x634, 0x1EE54 },
{ 0x635, 0x1EE51 },
{ 0x636, 0x1EE59 },
{ 0x639, 0x1EE4F },
{ 0x63A, 0x1EE5B },
{ 0x642, 0x1EE52 },
{ 0x644, 0x1EE4B },
{ 0x646, 0x1EE4D },
{ 0x64A, 0x1EE49 },
{ 0x66F, 0x1EE5F },
{ 0x6BA, 0x1EE5D }
};
static const MathVariantMapping arabicStretchedMapTable[] = {
{ 0x628, 0x1EE61 },
{ 0x62A, 0x1EE75 },
{ 0x62B, 0x1EE76 },
{ 0x62C, 0x1EE62 },
{ 0x62D, 0x1EE67 },
{ 0x62E, 0x1EE77 },
{ 0x633, 0x1EE6E },
{ 0x634, 0x1EE74 },
{ 0x635, 0x1EE71 },
{ 0x636, 0x1EE79 },
{ 0x637, 0x1EE68 },
{ 0x638, 0x1EE7A },
{ 0x639, 0x1EE6F },
{ 0x63A, 0x1EE7B },
{ 0x641, 0x1EE70 },
{ 0x642, 0x1EE72 },
{ 0x643, 0x1EE6A },
{ 0x645, 0x1EE6C },
{ 0x646, 0x1EE6D },
{ 0x647, 0x1EE64 },
{ 0x64A, 0x1EE69 },
{ 0x66E, 0x1EE7C },
{ 0x6A1, 0x1EE7E }
};
static const MathVariantMapping arabicLoopedMapTable[] = {
{ 0x627, 0x1EE80 },
{ 0x628, 0x1EE81 },
{ 0x62A, 0x1EE95 },
{ 0x62B, 0x1EE96 },
{ 0x62C, 0x1EE82 },
{ 0x62D, 0x1EE87 },
{ 0x62E, 0x1EE97 },
{ 0x62F, 0x1EE83 },
{ 0x630, 0x1EE98 },
{ 0x631, 0x1EE93 },
{ 0x632, 0x1EE86 },
{ 0x633, 0x1EE8E },
{ 0x634, 0x1EE94 },
{ 0x635, 0x1EE91 },
{ 0x636, 0x1EE99 },
{ 0x637, 0x1EE88 },
{ 0x638, 0x1EE9A },
{ 0x639, 0x1EE8F },
{ 0x63A, 0x1EE9B },
{ 0x641, 0x1EE90 },
{ 0x642, 0x1EE92 },
{ 0x644, 0x1EE8B },
{ 0x645, 0x1EE8C },
{ 0x646, 0x1EE8D },
{ 0x647, 0x1EE84 },
{ 0x648, 0x1EE85 },
{ 0x64A, 0x1EE89 }
};
static const MathVariantMapping arabicDoubleMapTable[] = {
{ 0x628, 0x1EEA1 },
{ 0x62A, 0x1EEB5 },
{ 0x62B, 0x1EEB6 },
{ 0x62C, 0x1EEA2 },
{ 0x62D, 0x1EEA7 },
{ 0x62E, 0x1EEB7 },
{ 0x62F, 0x1EEA3 },
{ 0x630, 0x1EEB8 },
{ 0x631, 0x1EEB3 },
{ 0x632, 0x1EEA6 },
{ 0x633, 0x1EEAE },
{ 0x634, 0x1EEB4 },
{ 0x635, 0x1EEB1 },
{ 0x636, 0x1EEB9 },
{ 0x637, 0x1EEA8 },
{ 0x638, 0x1EEBA },
{ 0x639, 0x1EEAF },
{ 0x63A, 0x1EEBB },
{ 0x641, 0x1EEB0 },
{ 0x642, 0x1EEB2 },
{ 0x644, 0x1EEAB },
{ 0x645, 0x1EEAC },
{ 0x646, 0x1EEAD },
{ 0x648, 0x1EEA5 },
{ 0x64A, 0x1EEA9 }
};
static const MathVariantMapping latinExceptionMapTable[] = {
{ 0x1D455, 0x210E },
{ 0x1D49D, 0x212C },
{ 0x1D4A0, 0x2130 },
{ 0x1D4A1, 0x2131 },
{ 0x1D4A3, 0x210B },
{ 0x1D4A4, 0x2110 },
{ 0x1D4A7, 0x2112 },
{ 0x1D4A8, 0x2133 },
{ 0x1D4AD, 0x211B },
{ 0x1D4BA, 0x212F },
{ 0x1D4BC, 0x210A },
{ 0x1D4C4, 0x2134 },
{ 0x1D506, 0x212D },
{ 0x1D50B, 0x210C },
{ 0x1D50C, 0x2111 },
{ 0x1D515, 0x211C },
{ 0x1D51D, 0x2128 },
{ 0x1D53A, 0x2102 },
{ 0x1D53F, 0x210D },
{ 0x1D545, 0x2115 },
{ 0x1D547, 0x2119 },
{ 0x1D548, 0x211A },
{ 0x1D549, 0x211D },
{ 0x1D551, 0x2124 }
};
const UChar32 greekUpperTheta = 0x03F4;
const UChar32 holeGreekUpperTheta = 0x03A2;
const UChar32 nabla = 0x2207;
const UChar32 partialDifferential = 0x2202;
const UChar32 greekUpperAlpha = 0x0391;
const UChar32 greekUpperOmega = 0x03A9;
const UChar32 greekLowerAlpha = 0x03B1;
const UChar32 greekLowerOmega = 0x03C9;
const UChar32 greekLunateEpsilonSymbol = 0x03F5;
const UChar32 greekThetaSymbol = 0x03D1;
const UChar32 greekKappaSymbol = 0x03F0;
const UChar32 greekPhiSymbol = 0x03D5;
const UChar32 greekRhoSymbol = 0x03F1;
const UChar32 greekPiSymbol = 0x03D6;
const UChar32 greekLetterDigamma = 0x03DC;
const UChar32 greekSmallLetterDigamma = 0x03DD;
const UChar32 mathBoldCapitalDigamma = 0x1D7CA;
const UChar32 mathBoldSmallDigamma = 0x1D7CB;
const UChar32 latinSmallLetterDotlessI = 0x0131;
const UChar32 latinSmallLetterDotlessJ = 0x0237;
const UChar32 mathItalicSmallDotlessI = 0x1D6A4;
const UChar32 mathItalicSmallDotlessJ = 0x1D6A5;
const UChar32 mathBoldUpperA = 0x1D400;
const UChar32 mathItalicUpperA = 0x1D434;
const UChar32 mathBoldSmallA = 0x1D41A;
const UChar32 mathBoldUpperAlpha = 0x1D6A8;
const UChar32 mathBoldSmallAlpha = 0x1D6C2;
const UChar32 mathItalicUpperAlpha = 0x1D6E2;
const UChar32 mathBoldDigitZero = 0x1D7CE;
const UChar32 mathDoubleStruckZero = 0x1D7D8;
const UChar32 mathBoldUpperTheta = 0x1D6B9;
const UChar32 mathBoldNabla = 0x1D6C1;
const UChar32 mathBoldPartialDifferential = 0x1D6DB;
const UChar32 mathBoldEpsilonSymbol = 0x1D6DC;
const UChar32 mathBoldThetaSymbol = 0x1D6DD;
const UChar32 mathBoldKappaSymbol = 0x1D6DE;
const UChar32 mathBoldPhiSymbol = 0x1D6DF;
const UChar32 mathBoldRhoSymbol = 0x1D6E0;
const UChar32 mathBoldPiSymbol = 0x1D6E1;
static UChar32 mathVariant(UChar32 codePoint, MathMLElement::MathVariant mathvariant)
{
ASSERT(mathvariant >= MathMLElement::MathVariant::Normal && mathvariant <= MathMLElement::MathVariant::Stretched);
if (mathvariant == MathMLElement::MathVariant::Normal)
return codePoint;
if (codePoint == holeGreekUpperTheta)
return codePoint; if (codePoint == greekLetterDigamma) {
if (mathvariant == MathMLElement::MathVariant::Bold)
return mathBoldCapitalDigamma;
return codePoint;
}
if (codePoint == greekSmallLetterDigamma) {
if (mathvariant == MathMLElement::MathVariant::Bold)
return mathBoldSmallDigamma;
return codePoint;
}
if (codePoint == latinSmallLetterDotlessI) {
if (mathvariant == MathMLElement::MathVariant::Italic)
return mathItalicSmallDotlessI;
return codePoint;
}
if (codePoint == latinSmallLetterDotlessJ) {
if (mathvariant == MathMLElement::MathVariant::Italic)
return mathItalicSmallDotlessJ;
return codePoint;
}
UChar32 baseChar = 0;
enum CharacterType {
Latin,
Greekish,
Number,
Arabic
};
CharacterType varType;
if (isASCIIUpper(codePoint)) {
baseChar = codePoint - 'A';
varType = Latin;
} else if (isASCIILower(codePoint)) {
baseChar = mathBoldSmallA - mathBoldUpperA + codePoint - 'a';
varType = Latin;
} else if (isASCIIDigit(codePoint)) {
baseChar = codePoint - '0';
varType = Number;
} else if (greekUpperAlpha <= codePoint && codePoint <= greekUpperOmega) {
baseChar = codePoint - greekUpperAlpha;
varType = Greekish;
} else if (greekLowerAlpha <= codePoint && codePoint <= greekLowerOmega) {
baseChar = mathBoldSmallAlpha - mathBoldUpperAlpha + codePoint - greekLowerAlpha;
varType = Greekish;
} else if (0x0600 <= codePoint && codePoint <= 0x06FF) {
varType = Arabic;
} else {
switch (codePoint) {
case greekUpperTheta:
baseChar = mathBoldUpperTheta - mathBoldUpperAlpha;
break;
case nabla:
baseChar = mathBoldNabla - mathBoldUpperAlpha;
break;
case partialDifferential:
baseChar = mathBoldPartialDifferential - mathBoldUpperAlpha;
break;
case greekLunateEpsilonSymbol:
baseChar = mathBoldEpsilonSymbol - mathBoldUpperAlpha;
break;
case greekThetaSymbol:
baseChar = mathBoldThetaSymbol - mathBoldUpperAlpha;
break;
case greekKappaSymbol:
baseChar = mathBoldKappaSymbol - mathBoldUpperAlpha;
break;
case greekPhiSymbol:
baseChar = mathBoldPhiSymbol - mathBoldUpperAlpha;
break;
case greekRhoSymbol:
baseChar = mathBoldRhoSymbol - mathBoldUpperAlpha;
break;
case greekPiSymbol:
baseChar = mathBoldPiSymbol - mathBoldUpperAlpha;
break;
default:
return codePoint;
}
varType = Greekish;
}
int8_t multiplier;
if (varType == Number) {
switch (mathvariant) {
case MathMLElement::MathVariant::Bold:
multiplier = 0;
break;
case MathMLElement::MathVariant::DoubleStruck:
multiplier = 1;
break;
case MathMLElement::MathVariant::SansSerif:
multiplier = 2;
break;
case MathMLElement::MathVariant::BoldSansSerif:
multiplier = 3;
break;
case MathMLElement::MathVariant::Monospace:
multiplier = 4;
break;
default:
return codePoint;
}
return baseChar + multiplier * (mathDoubleStruckZero - mathBoldDigitZero) + mathBoldDigitZero;
}
if (varType == Greekish) {
switch (mathvariant) {
case MathMLElement::MathVariant::Bold:
multiplier = 0;
break;
case MathMLElement::MathVariant::Italic:
multiplier = 1;
break;
case MathMLElement::MathVariant::BoldItalic:
multiplier = 2;
break;
case MathMLElement::MathVariant::BoldSansSerif:
multiplier = 3;
break;
case MathMLElement::MathVariant::SansSerifBoldItalic:
multiplier = 4;
break;
default:
return codePoint;
}
return baseChar + mathBoldUpperAlpha + multiplier * (mathItalicUpperAlpha - mathBoldUpperAlpha);
}
UChar32 tempChar = 0;
UChar32 newChar;
if (varType == Arabic) {
const MathVariantMapping* mapTable;
size_t tableLength;
switch (mathvariant) {
case MathMLElement::MathVariant::Initial:
mapTable = arabicInitialMapTable;
tableLength = WTF_ARRAY_LENGTH(arabicInitialMapTable);
break;
case MathMLElement::MathVariant::Tailed:
mapTable = arabicTailedMapTable;
tableLength = WTF_ARRAY_LENGTH(arabicTailedMapTable);
break;
case MathMLElement::MathVariant::Stretched:
mapTable = arabicStretchedMapTable;
tableLength = WTF_ARRAY_LENGTH(arabicStretchedMapTable);
break;
case MathMLElement::MathVariant::Looped:
mapTable = arabicLoopedMapTable;
tableLength = WTF_ARRAY_LENGTH(arabicLoopedMapTable);
break;
case MathMLElement::MathVariant::DoubleStruck:
mapTable = arabicDoubleMapTable;
tableLength = WTF_ARRAY_LENGTH(arabicDoubleMapTable);
break;
default:
return codePoint; }
newChar = MathVariantMappingSearch(codePoint, mapTable, tableLength);
} else {
if (mathvariant > MathMLElement::MathVariant::Monospace)
return codePoint; multiplier = static_cast<int>(mathvariant) - 2;
tempChar = baseChar + mathBoldUpperA + multiplier * (mathItalicUpperA - mathBoldUpperA);
newChar = MathVariantMappingSearch(tempChar, latinExceptionMapTable, WTF_ARRAY_LENGTH(latinExceptionMapTable));
}
if (newChar)
return newChar;
if (varType == Latin)
return tempChar;
return codePoint; }
void RenderMathMLToken::computePreferredLogicalWidths()
{
ASSERT(preferredLogicalWidthsDirty());
if (m_mathVariantGlyphDirty)
updateMathVariantGlyph();
if (m_mathVariantCodePoint) {
auto mathVariantGlyph = style().fontCascade().glyphDataForCharacter(m_mathVariantCodePoint.value(), m_mathVariantIsMirrored);
if (mathVariantGlyph.font) {
m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = mathVariantGlyph.font->widthForGlyph(mathVariantGlyph.glyph);
setPreferredLogicalWidthsDirty(false);
return;
}
}
RenderMathMLBlock::computePreferredLogicalWidths();
}
void RenderMathMLToken::updateMathVariantGlyph()
{
ASSERT(m_mathVariantGlyphDirty);
m_mathVariantCodePoint = std::nullopt;
m_mathVariantGlyphDirty = false;
if (const auto& block = downcast<RenderElement>(firstChild())) {
if (childrenOfType<RenderElement>(*block).first())
return;
}
const auto& tokenElement = element();
if (auto codePoint = MathMLTokenElement::convertToSingleCodePoint(element().textContent())) {
MathMLElement::MathVariant mathvariant = mathMLStyle().mathVariant();
if (mathvariant == MathMLElement::MathVariant::None)
mathvariant = tokenElement.hasTagName(MathMLNames::miTag) ? MathMLElement::MathVariant::Italic : MathMLElement::MathVariant::Normal;
UChar32 transformedCodePoint = mathVariant(codePoint.value(), mathvariant);
if (transformedCodePoint != codePoint.value()) {
m_mathVariantCodePoint = mathVariant(codePoint.value(), mathvariant);
m_mathVariantIsMirrored = !style().isLeftToRightDirection();
}
}
}
void RenderMathMLToken::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderMathMLBlock::styleDidChange(diff, oldStyle);
setMathVariantGlyphDirty();
}
void RenderMathMLToken::updateFromElement()
{
RenderMathMLBlock::updateFromElement();
setMathVariantGlyphDirty();
}
std::optional<int> RenderMathMLToken::firstLineBaseline() const
{
if (m_mathVariantCodePoint) {
auto mathVariantGlyph = style().fontCascade().glyphDataForCharacter(m_mathVariantCodePoint.value(), m_mathVariantIsMirrored);
if (mathVariantGlyph.font)
return std::optional<int>(static_cast<int>(lroundf(-mathVariantGlyph.font->boundsForGlyph(mathVariantGlyph.glyph).y())));
}
return RenderMathMLBlock::firstLineBaseline();
}
void RenderMathMLToken::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight)
{
ASSERT(needsLayout());
if (!relayoutChildren && simplifiedLayout())
return;
GlyphData mathVariantGlyph;
if (m_mathVariantCodePoint)
mathVariantGlyph = style().fontCascade().glyphDataForCharacter(m_mathVariantCodePoint.value(), m_mathVariantIsMirrored);
if (!mathVariantGlyph.font) {
RenderMathMLBlock::layoutBlock(relayoutChildren, pageLogicalHeight);
return;
}
for (auto* child = firstChildBox(); child; child = child->nextSiblingBox())
child->layoutIfNeeded();
setLogicalWidth(mathVariantGlyph.font->widthForGlyph(mathVariantGlyph.glyph));
setLogicalHeight(mathVariantGlyph.font->boundsForGlyph(mathVariantGlyph.glyph).height());
clearNeedsLayout();
}
void RenderMathMLToken::paint(PaintInfo& info, const LayoutPoint& paintOffset)
{
RenderMathMLBlock::paint(info, paintOffset);
if (info.context().paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE || !m_mathVariantCodePoint)
return;
auto mathVariantGlyph = style().fontCascade().glyphDataForCharacter(m_mathVariantCodePoint.value(), m_mathVariantIsMirrored);
if (!mathVariantGlyph.font)
return;
GraphicsContextStateSaver stateSaver(info.context());
info.context().setFillColor(style().visitedDependentColor(CSSPropertyColor));
GlyphBuffer buffer;
buffer.add(mathVariantGlyph.glyph, mathVariantGlyph.font, mathVariantGlyph.font->widthForGlyph(mathVariantGlyph.glyph));
LayoutUnit glyphAscent = static_cast<int>(lroundf(-mathVariantGlyph.font->boundsForGlyph(mathVariantGlyph.glyph).y()));
info.context().drawGlyphs(style().fontCascade(), *mathVariantGlyph.font, buffer, 0, 1, paintOffset + location() + LayoutPoint(0, glyphAscent));
}
void RenderMathMLToken::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
{
if (m_mathVariantCodePoint) {
auto mathVariantGlyph = style().fontCascade().glyphDataForCharacter(m_mathVariantCodePoint.value(), m_mathVariantIsMirrored);
if (mathVariantGlyph.font)
return;
}
RenderMathMLBlock::paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect);
}
}
#endif // ENABLE(MATHML)