Changeset 203072 in webkit


Ignore:
Timestamp:
Jul 11, 2016 11:38:36 AM (8 years ago)
Author:
fred.wang@free.fr
Message:

Add support for mathvariants that cannot be emulated via CSS.
https://bugs.webkit.org/show_bug.cgi?id=108778

Patch by Frederic Wang <fwang@igalia.com> on 2016-07-11
Reviewed by Brent Fulgham.

Source/WebCore:

Tests: mathml/mathml-in-html5/mathvariant-transforms-1.html

mathml/mathml-in-html5/mathvariant-transforms-2.html
mathml/presentation/mathvariant-inheritance.html
mathml/presentation/mathvariant-tokens.html

We remove the old code to emulate partial mathvariant support via CSS and add support
for all mathvariant values using the technique used for implicit italic on <mi> element.
We also rely on the MathMLStyle class introduced earlier to support custome MathML style
and manage inheritance of mathvariant values.
The function that tries and converts one base character into a transformed mathvariant
character is based on similar code from Gecko:
http://hg.mozilla.org/mozilla-central/file/tip/layout/generic/MathMLTextRunFactory.cpp
Note that we only support transform on token elements with a single character, which
should cover the most important use cases.

  • css/mathml.css: Remove the CSS rules to emulate some mathvariant values.

(math[mathvariant="normal"], mstyle[mathvariant="normal"], mo[mathvariant="normal"], mn[mathvariant="normal"], mi[mathvariant="normal"], mtext[mathvariant="normal"], mspace[mathvariant="normal"], ms[mathvariant="normal"]): Deleted.
(math[mathvariant="bold"], mstyle[mathvariant="bold"], mo[mathvariant="bold"], mn[mathvariant="bold"], mi[mathvariant="bold"], mtext[mathvariant="bold"], mspace[mathvariant="bold"], ms[mathvariant="bold"]): Deleted.
(math[mathvariant="italic"], mstyle[mathvariant="italic"], mo[mathvariant="italic"], mn[mathvariant="italic"], mi[mathvariant="italic"], mtext[mathvariant="italic"], mspace[mathvariant="italic"], ms[mathvariant="italic"]): Deleted.
(math[mathvariant="bold-italic"], mstyle[mathvariant="bold-italic"], mo[mathvariant="bold-italic"], mn[mathvariant="bold-italic"], mi[mathvariant="bold-italic"], mtext[mathvariant="bold-italic"], mspace[mathvariant="bold-italic"], ms[mathvariant="bold-italic"]): Deleted.

  • mathml/MathMLInlineContainerElement.cpp: We resolve mathml style when mathvariant changes.

(WebCore::MathMLInlineContainerElement::parseAttribute):

  • mathml/MathMLMathElement.cpp: ditto.

(WebCore::MathMLMathElement::parseAttribute):

  • mathml/MathMLTextElement.cpp: ditto.

(WebCore::MathMLTextElement::parseAttribute):

  • rendering/mathml/MathMLStyle.cpp: Add mathvariant property to the MathML style.

(WebCore::MathMLStyle::MathMLStyle): Init mathvariant to none.
(WebCore::MathMLStyle::getMathMLStyle): Helper function to retrieve the MathML style on a renderer.
(WebCore::MathMLStyle::updateStyleIfNeeded): Take into account change of mathvariant.
(WebCore::MathMLStyle::parseMathVariant): Helper function to parse a mathvariant attribute.
(WebCore::MathMLStyle::resolveMathMLStyle): Take into account mathvariant value: it is None
by default, inherited and can be modified via an attribute on <math>, <mstyle> or token
elements. We also refactor a bit to share logic between displaystyle and mathvariant.
(WebCore::MathMLStyle::setDisplayStyle): Deleted.

  • rendering/mathml/MathMLStyle.h: Add mathvariant members and update declarations.
  • rendering/mathml/RenderMathMLOperator.cpp:

(WebCore::RenderMathMLOperator::updateTokenContent): Call the function from the parent class
to consider mathvariant on <mo>.

  • rendering/mathml/RenderMathMLToken.cpp:

We implement a mathVariant function to transform a base character into its transformed mathvariant:

  • There are some regularity that allows to perform this via simple linear transforms.
  • However, there are also many exceptions and we rely on some sorted MathVariantMapping

tables to handle these cases.
(WebCore::ExtractKey): Helper function to perform binary searches on MathVariant tables.
(WebCore::MathVariantMappingSearch): ditto.
(WebCore::mathVariant): New function to perform mathvariant transforms.
(WebCore::RenderMathMLToken::updateMathVariantGlyph): Use the mathVariant function to
perform all transformations, not just the italic one.
(WebCore::transformToItalic): Deleted. Replaced with the more general mathVariant function.

LayoutTests:

Import a test from the MathML in HTML5 test suite to perform an exhaustive
verification of all the mathvariant transforms allowed.
We also add some tests to verify inheritance of the mathvariant style, the
effect on each token element and dynamic modification of mathvariant.
Finally, we modify one test now that mathvariant is no longer emulated via CSS.

  • mathml/mathml-in-html5/fonts/mathvariant-transforms.woff: Added.
  • mathml/mathml-in-html5/mathvariant-transforms-1-expected.html: Added.
  • mathml/mathml-in-html5/mathvariant-transforms-1.html: Added.
  • mathml/mathml-in-html5/mathvariant-transforms-2-expected.html: Added.
  • mathml/mathml-in-html5/mathvariant-transforms-2.html: Added.
  • mathml/presentation/attributes-mathvariant-expected.html: Update this test now that

mathvariant is correctly implemented using character transforms.

  • mathml/presentation/mathvariant-inheritance-expected.html: Added.
  • mathml/presentation/mathvariant-inheritance.html: Added.
  • mathml/presentation/mathvariant-tokens-expected.html: Added.
  • mathml/presentation/mathvariant-tokens.html: Added.
  • mathml/presentation/mathvariant-dynamic.html: Added.
  • mathml/presentation/mathvariant-dynamic-expected.html: Added.
Location:
trunk
Files:
11 added
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r203071 r203072  
     12016-07-11  Frederic Wang  <fwang@igalia.com>
     2
     3        Add support for mathvariants that cannot be emulated via CSS.
     4        https://bugs.webkit.org/show_bug.cgi?id=108778
     5
     6        Reviewed by Brent Fulgham.
     7
     8        Import a test from the MathML in HTML5 test suite to perform an exhaustive
     9        verification of all the mathvariant transforms allowed.
     10        We also add some tests to verify inheritance of the mathvariant style, the
     11        effect on each token element and dynamic modification of mathvariant.
     12        Finally, we modify one test now that mathvariant is no longer emulated via CSS.
     13
     14        * mathml/mathml-in-html5/fonts/mathvariant-transforms.woff: Added.
     15        * mathml/mathml-in-html5/mathvariant-transforms-1-expected.html: Added.
     16        * mathml/mathml-in-html5/mathvariant-transforms-1.html: Added.
     17        * mathml/mathml-in-html5/mathvariant-transforms-2-expected.html: Added.
     18        * mathml/mathml-in-html5/mathvariant-transforms-2.html: Added.
     19        * mathml/presentation/attributes-mathvariant-expected.html: Update this test now that
     20        mathvariant is correctly implemented using character transforms.
     21        * mathml/presentation/mathvariant-inheritance-expected.html: Added.
     22        * mathml/presentation/mathvariant-inheritance.html: Added.
     23        * mathml/presentation/mathvariant-tokens-expected.html: Added.
     24        * mathml/presentation/mathvariant-tokens.html: Added.
     25        * mathml/presentation/mathvariant-dynamic.html: Added.
     26        * mathml/presentation/mathvariant-dynamic-expected.html: Added.
     27
    1282016-07-11  Ryan Haddad  <ryanhaddad@apple.com>
    229
  • trunk/LayoutTests/mathml/presentation/attributes-mathvariant-expected.html

    r202420 r203072  
    1212         Unicode block ("U+1D400 MATHEMATICAL BOLD CAPITAL",
    1313         "U+1D434 MATHEMATICAL ITALIC CAPITAL A" and
    14          "U+1D468 MATHEMATICAL BOLD ITALIC CAPITAL A") but the WebKit
    15          implementation relies on CSS style instead, except for implicit
    16          mathvariant italic on single-char mi. See also bug 108778.
     14         "U+1D468 MATHEMATICAL BOLD ITALIC CAPITAL A").
    1715      -->
    1816    <div>
    1917      <!-- FIXME: we add new lines to workaround a width/spacing bug. -->
    2018      <math>
    21         <mtext style="font-style: normal;">A</mtext>
     19        <mtext>A</mtext>
    2220      </math><br/>
    2321      <math>
    24         <mtext style="font-style: normal;">𝐴</mtext>
     22        <mtext>𝐴</mtext>
    2523      </math><br/>
    2624      <math>
    27         <mtext style="font-style: italic;">A</mtext>
     25        <mtext>𝐴</mtext>
    2826      </math><br/>
    2927      <math>
    30         <mtext style="font-weight: bold;">A</mtext>
     28        <mtext>𝐀</mtext>
    3129      </math><br/>
    3230      <math>
    33         <mtext style="font-weight: bold; font-style: italic;">A</mtext>
     31        <mtext>𝑨</mtext>
    3432      </math>
    3533    </div>
  • trunk/LayoutTests/platform/ios-simulator/TestExpectations

    r202907 r203072  
    675675mathml/presentation/attributes-mathvariant.html [ Skip ]
    676676mathml/presentation/tokenElements-mathvariant.html [ Skip ]
     677mathml/presentation/mathvariant-inheritance.html [ Skip ]
     678mathml/presentation/mathvariant-tokens.html [ Skip ]
     679
     680# The web font loading & relayout seems to be performed too late.
     681imported/mathml-in-html5/mathml/relations/css-styling/mathvariant-transforms-1.html [ Pass ImageOnlyFailure ]
    677682
    678683# <rdar://problem/19215305> ASSERT(m_cgFont.get()) fails in FontPlatformData::ctFont()
  • trunk/Source/WebCore/ChangeLog

    r203066 r203072  
     12016-07-11  Frederic Wang  <fwang@igalia.com>
     2
     3        Add support for mathvariants that cannot be emulated via CSS.
     4        https://bugs.webkit.org/show_bug.cgi?id=108778
     5
     6        Reviewed by Brent Fulgham.
     7
     8        Tests: mathml/mathml-in-html5/mathvariant-transforms-1.html
     9               mathml/mathml-in-html5/mathvariant-transforms-2.html
     10               mathml/presentation/mathvariant-inheritance.html
     11               mathml/presentation/mathvariant-tokens.html
     12
     13        We remove the old code to emulate partial mathvariant support via CSS and add support
     14        for all mathvariant values using the technique used for implicit italic on <mi> element.
     15        We also rely on the MathMLStyle class introduced earlier to support custome MathML style
     16        and manage inheritance of mathvariant values.
     17        The function that tries and converts one base character into a transformed mathvariant
     18        character is based on similar code from Gecko:
     19        http://hg.mozilla.org/mozilla-central/file/tip/layout/generic/MathMLTextRunFactory.cpp
     20        Note that we only support transform on token elements with a single character, which
     21        should cover the most important use cases.
     22
     23        * css/mathml.css: Remove the CSS rules to emulate some mathvariant values.
     24        (math[mathvariant="normal"], mstyle[mathvariant="normal"], mo[mathvariant="normal"], mn[mathvariant="normal"], mi[mathvariant="normal"], mtext[mathvariant="normal"], mspace[mathvariant="normal"], ms[mathvariant="normal"]): Deleted.
     25        (math[mathvariant="bold"], mstyle[mathvariant="bold"], mo[mathvariant="bold"], mn[mathvariant="bold"], mi[mathvariant="bold"], mtext[mathvariant="bold"], mspace[mathvariant="bold"], ms[mathvariant="bold"]): Deleted.
     26        (math[mathvariant="italic"], mstyle[mathvariant="italic"], mo[mathvariant="italic"], mn[mathvariant="italic"], mi[mathvariant="italic"], mtext[mathvariant="italic"], mspace[mathvariant="italic"], ms[mathvariant="italic"]): Deleted.
     27        (math[mathvariant="bold-italic"], mstyle[mathvariant="bold-italic"], mo[mathvariant="bold-italic"], mn[mathvariant="bold-italic"], mi[mathvariant="bold-italic"], mtext[mathvariant="bold-italic"], mspace[mathvariant="bold-italic"], ms[mathvariant="bold-italic"]): Deleted.
     28        * mathml/MathMLInlineContainerElement.cpp: We resolve mathml style when mathvariant changes.
     29        (WebCore::MathMLInlineContainerElement::parseAttribute):
     30        * mathml/MathMLMathElement.cpp: ditto.
     31        (WebCore::MathMLMathElement::parseAttribute):
     32        * mathml/MathMLTextElement.cpp: ditto.
     33        (WebCore::MathMLTextElement::parseAttribute):
     34        * rendering/mathml/MathMLStyle.cpp: Add mathvariant property to the MathML style.
     35        (WebCore::MathMLStyle::MathMLStyle): Init mathvariant to none.
     36        (WebCore::MathMLStyle::getMathMLStyle): Helper function to retrieve the MathML style on a renderer.
     37        (WebCore::MathMLStyle::updateStyleIfNeeded): Take into account change of mathvariant.
     38        (WebCore::MathMLStyle::parseMathVariant): Helper function to parse a mathvariant attribute.
     39        (WebCore::MathMLStyle::resolveMathMLStyle): Take into account mathvariant value: it is None
     40        by default, inherited and can be modified via an attribute on <math>, <mstyle> or token
     41        elements. We also refactor a bit to share logic between displaystyle and mathvariant.
     42        (WebCore::MathMLStyle::setDisplayStyle): Deleted.
     43        * rendering/mathml/MathMLStyle.h: Add mathvariant members and update declarations.
     44        * rendering/mathml/RenderMathMLOperator.cpp:
     45        (WebCore::RenderMathMLOperator::updateTokenContent): Call the function from the parent class
     46        to consider mathvariant on <mo>.
     47        * rendering/mathml/RenderMathMLToken.cpp:
     48        We implement a mathVariant function to transform a base character into its transformed mathvariant:
     49        - There are some regularity that allows to perform this via simple linear transforms.
     50        - However, there are also many exceptions and we rely on some sorted MathVariantMapping
     51        tables to handle these cases.
     52        (WebCore::ExtractKey): Helper function to perform binary searches on MathVariant tables.
     53        (WebCore::MathVariantMappingSearch): ditto.
     54        (WebCore::mathVariant): New function to perform mathvariant transforms.
     55        (WebCore::RenderMathMLToken::updateMathVariantGlyph): Use the mathVariant function to
     56        perform all transformations, not just the italic one.
     57        (WebCore::transformToItalic): Deleted. Replaced with the more general mathVariant function.
     58
    1592016-07-11  Jeremy Jones  <jeremyj@apple.com>
    260
  • trunk/Source/WebCore/css/mathml.css

    r202934 r203072  
    133133}
    134134
    135 math[mathvariant="normal"], mstyle[mathvariant="normal"], mo[mathvariant="normal"], mn[mathvariant="normal"], mi[mathvariant="normal"], mtext[mathvariant="normal"], mspace[mathvariant="normal"], ms[mathvariant="normal"] {
    136     font-style: normal;
    137     font-weight: normal;
    138 }
    139 
    140 math[mathvariant="bold"], mstyle[mathvariant="bold"], mo[mathvariant="bold"], mn[mathvariant="bold"], mi[mathvariant="bold"], mtext[mathvariant="bold"], mspace[mathvariant="bold"], ms[mathvariant="bold"] {
    141     font-style: normal;
    142     font-weight: bold;
    143 }
    144 
    145 math[mathvariant="italic"], mstyle[mathvariant="italic"], mo[mathvariant="italic"], mn[mathvariant="italic"], mi[mathvariant="italic"], mtext[mathvariant="italic"], mspace[mathvariant="italic"], ms[mathvariant="italic"] {
    146     font-style: italic;
    147     font-weight: normal;
    148 }
    149 
    150 math[mathvariant="bold-italic"], mstyle[mathvariant="bold-italic"], mo[mathvariant="bold-italic"], mn[mathvariant="bold-italic"], mi[mathvariant="bold-italic"], mtext[mathvariant="bold-italic"], mspace[mathvariant="bold-italic"], ms[mathvariant="bold-italic"] {
    151     font-weight: bold;
    152     font-style: italic;
    153 }
    154 
    155135math[mathsize="small"], mstyle[mathsize="small"], mo[mathsize="small"], mn[mathsize="small"], mi[mathsize="small"], mtext[mathsize="small"], mspace[mathsize="small"], ms[mathsize="small"] {
    156136    font-size: 0.75em;
  • trunk/Source/WebCore/mathml/MathMLInlineContainerElement.cpp

    r202962 r203072  
    8989void MathMLInlineContainerElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    9090{
    91     if (name == displaystyleAttr && (hasTagName(mstyleTag) || hasTagName(mtableTag)) && renderer())
     91    bool displayStyleAttribute = (name == displaystyleAttr && (hasTagName(mstyleTag) || hasTagName(mtableTag)));
     92    bool mathVariantAttribute = (name == mathvariantAttr && (hasTagName(mathTag) || hasTagName(mstyleTag)));
     93    if ((displayStyleAttribute || mathVariantAttribute) && renderer())
    9294        MathMLStyle::resolveMathMLStyleTree(renderer());
    9395
  • trunk/Source/WebCore/mathml/MathMLMathElement.cpp

    r202960 r203072  
    5656void MathMLMathElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    5757{
    58     if ((name == displaystyleAttr || name == displayAttr) && hasTagName(mathTag) && renderer())
     58    if ((name == displaystyleAttr || name == displayAttr || name == mathvariantAttr) && renderer())
    5959        MathMLStyle::resolveMathMLStyleTree(renderer());
    6060
  • trunk/Source/WebCore/mathml/MathMLTextElement.cpp

    r202970 r203072  
    7373    }
    7474
     75    if (name == mathvariantAttr && renderer())
     76        MathMLStyle::resolveMathMLStyleTree(renderer());
     77
    7578    MathMLElement::parseAttribute(name, value);
    7679}
  • trunk/Source/WebCore/rendering/mathml/MathMLStyle.cpp

    r202979 r203072  
    4848}
    4949
    50 void MathMLStyle::setDisplayStyle(RenderObject* renderer)
    51 {
    52     if (!renderer)
    53         return;
    54 
    55     // FIXME: Should we make RenderMathMLTable derive from RenderMathMLBlock in order to simplify this?
    56     if (is<RenderMathMLTable>(renderer))
    57         m_displayStyle = downcast<RenderMathMLTable>(renderer)->mathMLStyle()->displayStyle();
    58     else if (is<RenderMathMLBlock>(renderer))
    59         m_displayStyle = downcast<RenderMathMLBlock>(renderer)->mathMLStyle()->displayStyle();
     50const MathMLStyle* MathMLStyle::getMathMLStyle(RenderObject* renderer)
     51{
     52    if (renderer) {
     53        // FIXME: Should we make RenderMathMLTable derive from RenderMathMLBlock in order to simplify this?
     54        if (is<RenderMathMLTable>(renderer))
     55            return downcast<RenderMathMLTable>(renderer)->mathMLStyle();
     56        if (is<RenderMathMLBlock>(renderer))
     57            return downcast<RenderMathMLBlock>(renderer)->mathMLStyle();
     58    }
     59
     60    return nullptr;
    6061}
    6162
     
    8182}
    8283
    83 void MathMLStyle::updateStyleIfNeeded(RenderObject* renderer, bool oldDisplayStyle)
     84void MathMLStyle::updateStyleIfNeeded(RenderObject* renderer, bool oldDisplayStyle, MathVariant oldMathVariant)
    8485{
    8586    if (oldDisplayStyle != m_displayStyle) {
     
    9293            downcast<RenderMathMLFraction>(renderer)->updateFromElement();
    9394    }
     95    if (oldMathVariant != m_mathVariant) {
     96        if (is<RenderMathMLToken>(renderer))
     97            downcast<RenderMathMLToken>(renderer)->updateTokenContent();
     98    }
     99}
     100
     101MathMLStyle::MathVariant MathMLStyle::parseMathVariant(const AtomicString& attributeValue)
     102{
     103    if (attributeValue == "normal")
     104        return Normal;
     105    if (attributeValue == "bold")
     106        return Bold;
     107    if (attributeValue == "italic")
     108        return Italic;
     109    if (attributeValue == "bold-italic")
     110        return BoldItalic;
     111    if (attributeValue == "double-struck")
     112        return DoubleStruck;
     113    if (attributeValue == "bold-fraktur")
     114        return BoldFraktur;
     115    if (attributeValue == "script")
     116        return Script;
     117    if (attributeValue == "bold-script")
     118        return BoldScript;
     119    if (attributeValue == "fraktur")
     120        return Fraktur;
     121    if (attributeValue == "sans-serif")
     122        return SansSerif;
     123    if (attributeValue == "bold-sans-serif")
     124        return BoldSansSerif;
     125    if (attributeValue == "sans-serif-italic")
     126        return SansSerifItalic;
     127    if (attributeValue == "sans-serif-bold-italic")
     128        return SansSerifBoldItalic;
     129    if (attributeValue == "monospace")
     130        return Monospace;
     131    if (attributeValue == "initial")
     132        return Initial;
     133    if (attributeValue == "tailed")
     134        return Tailed;
     135    if (attributeValue == "looped")
     136        return Looped;
     137    if (attributeValue == "stretched")
     138        return Stretched;
     139    return None;
    94140}
    95141
     
    99145
    100146    bool oldDisplayStyle = m_displayStyle;
    101 
    102     // For anonymous renderers, we just inherit the style from our parent.
     147    MathVariant oldMathVariant = m_mathVariant;
     148    auto* parentRenderer = getMathMLParentNode(renderer);
     149    const MathMLStyle* parentStyle = getMathMLStyle(parentRenderer);
     150
     151    // By default, we just inherit the style from our parent.
     152    m_displayStyle = false;
     153    m_mathVariant = None;
     154    if (parentStyle) {
     155        setDisplayStyle(parentStyle->displayStyle());
     156        setMathVariant(parentStyle->mathVariant());
     157    }
     158
     159    // Early return for anonymous renderers.
    103160    if (renderer->isAnonymous()) {
    104         setDisplayStyle(getMathMLParentNode(renderer));
    105         updateStyleIfNeeded(renderer, oldDisplayStyle);
     161        updateStyleIfNeeded(renderer, oldDisplayStyle, oldMathVariant);
    106162        return;
    107163    }
     
    111167    else if (is<RenderMathMLTable>(renderer))
    112168        m_displayStyle = false; // The default displaystyle of <mtable> is false.
    113     else if (auto* parentRenderer = getMathMLParentNode(renderer)) {
    114         setDisplayStyle(parentRenderer); // The default displaystyle is inherited from our parent.
     169    else if (parentRenderer) {
    115170        if (is<RenderMathMLFraction>(parentRenderer))
    116171            m_displayStyle = false; // <mfrac> sets displaystyle to false within its numerator and denominator.
     
    135190    }
    136191
    137     updateStyleIfNeeded(renderer, oldDisplayStyle);
     192    // The mathvariant attribute on the <math>, <mstyle> or token elements overrides the default behavior.
     193    if (is<RenderMathMLMath>(renderer) || is<RenderMathMLToken>(renderer) || tagName == mstyleTag) {
     194        MathVariant mathvariant = parseMathVariant(element->fastGetAttribute(mathvariantAttr));
     195        if (mathvariant != None)
     196            m_mathVariant = mathvariant;
     197    }
     198
     199    updateStyleIfNeeded(renderer, oldDisplayStyle, oldMathVariant);
    138200}
    139201
  • trunk/Source/WebCore/rendering/mathml/MathMLStyle.h

    r202960 r203072  
    4343    void setDisplayStyle(bool displayStyle) { m_displayStyle = displayStyle; }
    4444
     45    // These are the mathvariant values from the MathML recommendation.
     46    // The special value none means that no explicit mathvariant value has been specified.
     47    // Note that the numeral values are important for the computation performed in the mathVariant function of RenderMathMLToken, do not change them!
     48    enum MathVariant {
     49        None = 0,
     50        Normal = 1,
     51        Bold = 2,
     52        Italic = 3,
     53        BoldItalic = 4,
     54        Script = 5,
     55        BoldScript = 6,
     56        Fraktur = 7,
     57        DoubleStruck = 8,
     58        BoldFraktur = 9,
     59        SansSerif = 10,
     60        BoldSansSerif = 11,
     61        SansSerifItalic = 12,
     62        SansSerifBoldItalic = 13,
     63        Monospace = 14,
     64        Initial = 15,
     65        Tailed = 16,
     66        Looped = 17,
     67        Stretched = 18
     68    };
     69    MathVariant mathVariant() const { return m_mathVariant; }
     70    void setMathVariant(MathVariant mathvariant) { m_mathVariant = mathvariant; }
     71
    4572    void resolveMathMLStyle(RenderObject*);
    4673    static void resolveMathMLStyleTree(RenderObject*);
     
    4875private:
    4976    bool isDisplayStyleAlwaysFalse(RenderObject*);
    50     void setDisplayStyle(RenderObject*);
     77    const MathMLStyle* getMathMLStyle(RenderObject* renderer);
    5178    RenderObject* getMathMLParentNode(RenderObject*);
    52     void updateStyleIfNeeded(RenderObject*, bool);
     79    void updateStyleIfNeeded(RenderObject*, bool, MathVariant);
     80    MathVariant parseMathVariant(const AtomicString& attributeValue);
    5381
    54     bool m_displayStyle = false;
     82    bool m_displayStyle { false };
     83    MathVariant m_mathVariant { None };
    5584};
    5685
  • trunk/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp

    r202973 r203072  
    326326{
    327327    ASSERT(!isAnonymous());
     328    RenderMathMLToken::updateTokenContent();
    328329    rebuildTokenContent(element().textContent());
    329330}
  • trunk/Source/WebCore/rendering/mathml/RenderMathMLToken.cpp

    r202420 r203072  
    5959}
    6060
    61 static bool transformToItalic(UChar32& codePoint)
    62 {
    63     const UChar32 lowerAlpha = 0x3B1;
    64     const UChar32 lowerOmega = 0x3C9;
    65     const UChar32 mathItalicLowerA = 0x1D44E;
    66     const UChar32 mathItalicLowerAlpha = 0x1D6FC;
    67     const UChar32 mathItalicLowerH = 0x210E;
    68     const UChar32 mathItalicUpperA = 0x1D434;
    69 
    70     // FIXME: We should also transform dotless i, dotless j and more greek letters.
    71     if ('a' <= codePoint && codePoint <= 'z') {
    72         if (codePoint == 'h')
    73             codePoint = mathItalicLowerH;
    74         else
    75             codePoint += mathItalicLowerA - 'a';
    76         return true;
    77     }
     61// Entries for the mathvariant lookup tables.
     62// 'key' represents the Unicode character to be transformed and is used for searching the tables.
     63// 'replacement' represents the mapped mathvariant Unicode character.
     64struct MathVariantMapping {
     65    uint32_t key;
     66    uint32_t replacement;
     67};
     68static inline UChar32 ExtractKey(const MathVariantMapping* entry) { return entry->key; }
     69static UChar32 MathVariantMappingSearch(uint32_t key, const MathVariantMapping* table, size_t tableLength)
     70{
     71    if (const auto* entry = tryBinarySearch<const MathVariantMapping, UChar32>(table, tableLength, key, ExtractKey))
     72        return entry->replacement;
     73
     74    return 0;
     75}
     76
     77// Lookup tables for use with mathvariant mappings to transform a unicode character point to another unicode character that indicates the proper output.
     78// key represents one of two concepts.
     79// 1. In the Latin table it represents a hole in the mathematical alphanumeric block, where the character that should occupy that position is located elsewhere.
     80// 2. It represents an Arabic letter.
     81//  As a replacement, 0 is reserved to indicate no mapping was found.
     82static const MathVariantMapping arabicInitialMapTable[] = {
     83    { 0x628, 0x1EE21 },
     84    { 0x62A, 0x1EE35 },
     85    { 0x62B, 0x1EE36 },
     86    { 0x62C, 0x1EE22 },
     87    { 0x62D, 0x1EE27 },
     88    { 0x62E, 0x1EE37 },
     89    { 0x633, 0x1EE2E },
     90    { 0x634, 0x1EE34 },
     91    { 0x635, 0x1EE31 },
     92    { 0x636, 0x1EE39 },
     93    { 0x639, 0x1EE2F },
     94    { 0x63A, 0x1EE3B },
     95    { 0x641, 0x1EE30 },
     96    { 0x642, 0x1EE32 },
     97    { 0x643, 0x1EE2A },
     98    { 0x644, 0x1EE2B },
     99    { 0x645, 0x1EE2C },
     100    { 0x646, 0x1EE2D },
     101    { 0x647, 0x1EE24 },
     102    { 0x64A, 0x1EE29 }
     103};
     104
     105static const MathVariantMapping arabicTailedMapTable[] = {
     106    { 0x62C, 0x1EE42 },
     107    { 0x62D, 0x1EE47 },
     108    { 0x62E, 0x1EE57 },
     109    { 0x633, 0x1EE4E },
     110    { 0x634, 0x1EE54 },
     111    { 0x635, 0x1EE51 },
     112    { 0x636, 0x1EE59 },
     113    { 0x639, 0x1EE4F },
     114    { 0x63A, 0x1EE5B },
     115    { 0x642, 0x1EE52 },
     116    { 0x644, 0x1EE4B },
     117    { 0x646, 0x1EE4D },
     118    { 0x64A, 0x1EE49 },
     119    { 0x66F, 0x1EE5F },
     120    { 0x6BA, 0x1EE5D }
     121};
     122
     123static const MathVariantMapping arabicStretchedMapTable[] = {
     124    { 0x628, 0x1EE61 },
     125    { 0x62A, 0x1EE75 },
     126    { 0x62B, 0x1EE76 },
     127    { 0x62C, 0x1EE62 },
     128    { 0x62D, 0x1EE67 },
     129    { 0x62E, 0x1EE77 },
     130    { 0x633, 0x1EE6E },
     131    { 0x634, 0x1EE74 },
     132    { 0x635, 0x1EE71 },
     133    { 0x636, 0x1EE79 },
     134    { 0x637, 0x1EE68 },
     135    { 0x638, 0x1EE7A },
     136    { 0x639, 0x1EE6F },
     137    { 0x63A, 0x1EE7B },
     138    { 0x641, 0x1EE70 },
     139    { 0x642, 0x1EE72 },
     140    { 0x643, 0x1EE6A },
     141    { 0x645, 0x1EE6C },
     142    { 0x646, 0x1EE6D },
     143    { 0x647, 0x1EE64 },
     144    { 0x64A, 0x1EE69 },
     145    { 0x66E, 0x1EE7C },
     146    { 0x6A1, 0x1EE7E }
     147};
     148
     149static const MathVariantMapping arabicLoopedMapTable[] = {
     150    { 0x627, 0x1EE80 },
     151    { 0x628, 0x1EE81 },
     152    { 0x62A, 0x1EE95 },
     153    { 0x62B, 0x1EE96 },
     154    { 0x62C, 0x1EE82 },
     155    { 0x62D, 0x1EE87 },
     156    { 0x62E, 0x1EE97 },
     157    { 0x62F, 0x1EE83 },
     158    { 0x630, 0x1EE98 },
     159    { 0x631, 0x1EE93 },
     160    { 0x632, 0x1EE86 },
     161    { 0x633, 0x1EE8E },
     162    { 0x634, 0x1EE94 },
     163    { 0x635, 0x1EE91 },
     164    { 0x636, 0x1EE99 },
     165    { 0x637, 0x1EE88 },
     166    { 0x638, 0x1EE9A },
     167    { 0x639, 0x1EE8F },
     168    { 0x63A, 0x1EE9B },
     169    { 0x641, 0x1EE90 },
     170    { 0x642, 0x1EE92 },
     171    { 0x644, 0x1EE8B },
     172    { 0x645, 0x1EE8C },
     173    { 0x646, 0x1EE8D },
     174    { 0x647, 0x1EE84 },
     175    { 0x648, 0x1EE85 },
     176    { 0x64A, 0x1EE89 }
     177};
     178
     179static const MathVariantMapping arabicDoubleMapTable[] = {
     180    { 0x628, 0x1EEA1 },
     181    { 0x62A, 0x1EEB5 },
     182    { 0x62B, 0x1EEB6 },
     183    { 0x62C, 0x1EEA2 },
     184    { 0x62D, 0x1EEA7 },
     185    { 0x62E, 0x1EEB7 },
     186    { 0x62F, 0x1EEA3 },
     187    { 0x630, 0x1EEB8 },
     188    { 0x631, 0x1EEB3 },
     189    { 0x632, 0x1EEA6 },
     190    { 0x633, 0x1EEAE },
     191    { 0x634, 0x1EEB4 },
     192    { 0x635, 0x1EEB1 },
     193    { 0x636, 0x1EEB9 },
     194    { 0x637, 0x1EEA8 },
     195    { 0x638, 0x1EEBA },
     196    { 0x639, 0x1EEAF },
     197    { 0x63A, 0x1EEBB },
     198    { 0x641, 0x1EEB0 },
     199    { 0x642, 0x1EEB2 },
     200    { 0x644, 0x1EEAB },
     201    { 0x645, 0x1EEAC },
     202    { 0x646, 0x1EEAD },
     203    { 0x648, 0x1EEA5 },
     204    { 0x64A, 0x1EEA9 }
     205};
     206
     207static const MathVariantMapping latinExceptionMapTable[] = {
     208    { 0x1D455, 0x210E },
     209    { 0x1D49D, 0x212C },
     210    { 0x1D4A0, 0x2130 },
     211    { 0x1D4A1, 0x2131 },
     212    { 0x1D4A3, 0x210B },
     213    { 0x1D4A4, 0x2110 },
     214    { 0x1D4A7, 0x2112 },
     215    { 0x1D4A8, 0x2133 },
     216    { 0x1D4AD, 0x211B },
     217    { 0x1D4BA, 0x212F },
     218    { 0x1D4BC, 0x210A },
     219    { 0x1D4C4, 0x2134 },
     220    { 0x1D506, 0x212D },
     221    { 0x1D50B, 0x210C },
     222    { 0x1D50C, 0x2111 },
     223    { 0x1D515, 0x211C },
     224    { 0x1D51D, 0x2128 },
     225    { 0x1D53A, 0x2102 },
     226    { 0x1D53F, 0x210D },
     227    { 0x1D545, 0x2115 },
     228    { 0x1D547, 0x2119 },
     229    { 0x1D548, 0x211A },
     230    { 0x1D549, 0x211D },
     231    { 0x1D551, 0x2124 }
     232};
     233
     234const UChar32 greekUpperTheta = 0x03F4;
     235const UChar32 holeGreekUpperTheta = 0x03A2;
     236const UChar32 nabla = 0x2207;
     237const UChar32 partialDifferential = 0x2202;
     238const UChar32 greekUpperAlpha = 0x0391;
     239const UChar32 greekUpperOmega = 0x03A9;
     240const UChar32 greekLowerAlpha = 0x03B1;
     241const UChar32 greekLowerOmega = 0x03C9;
     242const UChar32 greekLunateEpsilonSymbol = 0x03F5;
     243const UChar32 greekThetaSymbol = 0x03D1;
     244const UChar32 greekKappaSymbol = 0x03F0;
     245const UChar32 greekPhiSymbol = 0x03D5;
     246const UChar32 greekRhoSymbol = 0x03F1;
     247const UChar32 greekPiSymbol = 0x03D6;
     248const UChar32 greekLetterDigamma = 0x03DC;
     249const UChar32 greekSmallLetterDigamma = 0x03DD;
     250const UChar32 mathBoldCapitalDigamma = 0x1D7CA;
     251const UChar32 mathBoldSmallDigamma = 0x1D7CB;
     252
     253const UChar32 latinSmallLetterDotlessI = 0x0131;
     254const UChar32 latinSmallLetterDotlessJ = 0x0237;
     255
     256const UChar32 mathItalicSmallDotlessI = 0x1D6A4;
     257const UChar32 mathItalicSmallDotlessJ = 0x1D6A5;
     258
     259const UChar32 mathBoldUpperA = 0x1D400;
     260const UChar32 mathItalicUpperA = 0x1D434;
     261const UChar32 mathBoldSmallA = 0x1D41A;
     262const UChar32 mathBoldUpperAlpha = 0x1D6A8;
     263const UChar32 mathBoldSmallAlpha = 0x1D6C2;
     264const UChar32 mathItalicUpperAlpha = 0x1D6E2;
     265const UChar32 mathBoldDigitZero = 0x1D7CE;
     266const UChar32 mathDoubleStruckZero = 0x1D7D8;
     267
     268const UChar32 mathBoldUpperTheta = 0x1D6B9;
     269const UChar32 mathBoldNabla = 0x1D6C1;
     270const UChar32 mathBoldPartialDifferential = 0x1D6DB;
     271const UChar32 mathBoldEpsilonSymbol = 0x1D6DC;
     272const UChar32 mathBoldThetaSymbol = 0x1D6DD;
     273const UChar32 mathBoldKappaSymbol = 0x1D6DE;
     274const UChar32 mathBoldPhiSymbol = 0x1D6DF;
     275const UChar32 mathBoldRhoSymbol = 0x1D6E0;
     276const UChar32 mathBoldPiSymbol = 0x1D6E1;
     277
     278// Performs the character mapping needed to implement MathML's mathvariant attribute.
     279// It takes a unicode character and maps it to its appropriate mathvariant counterpart specified by mathvariant.
     280// The mapped character is typically located within Unicode's mathematical blocks (0x1D***, 0x1EE**) but there are exceptions which this function accounts for.
     281// Characters without a valid mapping or valid aMathvar value are returned
     282// unaltered.
     283// Characters already in the mathematical blocks (or are one of the exceptions) are never transformed.
     284// Acceptable values for mathvariant are specified in MathMLStyle.h
     285// The transformable characters can be found at:
     286// http://lists.w3.org/Archives/Public/www-math/2013Sep/0012.html and
     287// https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols
     288static UChar32 mathVariant(UChar32 codePoint, MathMLStyle::MathVariant mathvariant)
     289{
     290    ASSERT(mathvariant >= MathMLStyle::Normal && mathvariant <= MathMLStyle::Stretched);
     291
     292    if (mathvariant == MathMLStyle::Normal)
     293        return codePoint; // Nothing to do here.
     294
     295    // Exceptional characters with at most one possible transformation.
     296    if (codePoint == holeGreekUpperTheta)
     297        return codePoint; // Nothing at this code point is transformed
     298    if (codePoint == greekLetterDigamma) {
     299        if (mathvariant == MathMLStyle::Bold)
     300            return mathBoldCapitalDigamma;
     301        return codePoint;
     302    }
     303    if (codePoint == greekSmallLetterDigamma) {
     304        if (mathvariant == MathMLStyle::Bold)
     305            return mathBoldSmallDigamma;
     306        return codePoint;
     307    }
     308    if (codePoint == latinSmallLetterDotlessI) {
     309        if (mathvariant == MathMLStyle::Italic)
     310            return mathItalicSmallDotlessI;
     311        return codePoint;
     312    }
     313    if (codePoint == latinSmallLetterDotlessJ) {
     314        if (mathvariant == MathMLStyle::Italic)
     315            return mathItalicSmallDotlessJ;
     316        return codePoint;
     317    }
     318
     319    // The Unicode mathematical blocks are divided into four segments: Latin, Greek, numbers and Arabic.
     320    // In the case of the first three baseChar represents the relative order in which the characters are encoded in the Unicode mathematical block, normalised to the first character of that sequence.
     321    UChar32 baseChar;
     322    enum CharacterType {
     323        Latin,
     324        Greekish,
     325        Number,
     326        Arabic
     327    };
     328    CharacterType varType;
    78329    if ('A' <= codePoint && codePoint <= 'Z') {
    79         codePoint += mathItalicUpperA - 'A';
    80         return true;
    81     }
    82     if (lowerAlpha <= codePoint && codePoint <= lowerOmega) {
    83         codePoint += mathItalicLowerAlpha - lowerAlpha;
    84         return true;
    85     }
    86 
    87     return false;
     330        baseChar = codePoint - 'A';
     331        varType = Latin;
     332    } else if ('a' <= codePoint && codePoint <= 'z') {
     333        // Lowercase characters are placed immediately after the uppercase characters in the Unicode mathematical block.
     334        // The constant subtraction represents the number of characters between the start of the sequence (capital A) and the first lowercase letter.
     335        baseChar = mathBoldSmallA - mathBoldUpperA + codePoint - 'a';
     336        varType = Latin;
     337    } else if ('0' <= codePoint && codePoint <= '9') {
     338        baseChar = codePoint - '0';
     339        varType = Number;
     340    } else if (greekUpperAlpha <= codePoint && codePoint <= greekUpperOmega) {
     341        baseChar = codePoint - greekUpperAlpha;
     342        varType = Greekish;
     343    } else if (greekLowerAlpha <= codePoint && codePoint <= greekLowerOmega) {
     344        // Lowercase Greek comes after uppercase Greek.
     345        // Note in this instance the presence of an additional character (Nabla) between the end of the uppercase Greek characters and the lowercase ones.
     346        baseChar = mathBoldSmallAlpha - mathBoldUpperAlpha + codePoint - greekLowerAlpha;
     347        varType = Greekish;
     348    } else if (0x0600 <= codePoint && codePoint <= 0x06FF) {
     349        // Arabic characters are defined within this range
     350        varType = Arabic;
     351    } else {
     352        switch (codePoint) {
     353        case greekUpperTheta:
     354            baseChar = mathBoldUpperTheta - mathBoldUpperAlpha;
     355            break;
     356        case nabla:
     357            baseChar = mathBoldNabla - mathBoldUpperAlpha;
     358            break;
     359        case partialDifferential:
     360            baseChar = mathBoldPartialDifferential - mathBoldUpperAlpha;
     361            break;
     362        case greekLunateEpsilonSymbol:
     363            baseChar = mathBoldEpsilonSymbol - mathBoldUpperAlpha;
     364            break;
     365        case greekThetaSymbol:
     366            baseChar = mathBoldThetaSymbol - mathBoldUpperAlpha;
     367            break;
     368        case greekKappaSymbol:
     369            baseChar = mathBoldKappaSymbol - mathBoldUpperAlpha;
     370            break;
     371        case greekPhiSymbol:
     372            baseChar = mathBoldPhiSymbol - mathBoldUpperAlpha;
     373            break;
     374        case greekRhoSymbol:
     375            baseChar = mathBoldRhoSymbol - mathBoldUpperAlpha;
     376            break;
     377        case greekPiSymbol:
     378            baseChar = mathBoldPiSymbol - mathBoldUpperAlpha;
     379            break;
     380        default:
     381            return codePoint;
     382        }
     383        varType = Greekish;
     384    }
     385
     386    int8_t multiplier;
     387    if (varType == Number) {
     388        // Each possible number mathvariant is encoded in a single, contiguous block.
     389        // For example the beginning of the double struck number range follows immediately after the end of the bold number range.
     390        // multiplier represents the order of the sequences relative to the first one.
     391        switch (mathvariant) {
     392        case MathMLStyle::Bold:
     393            multiplier = 0;
     394            break;
     395        case MathMLStyle::DoubleStruck:
     396            multiplier = 1;
     397            break;
     398        case MathMLStyle::SansSerif:
     399            multiplier = 2;
     400            break;
     401        case MathMLStyle::BoldSansSerif:
     402            multiplier = 3;
     403            break;
     404        case MathMLStyle::Monospace:
     405            multiplier = 4;
     406            break;
     407        default:
     408            // This mathvariant isn't defined for numbers or is otherwise normal.
     409            return codePoint;
     410        }
     411        // As the ranges are contiguous, to find the desired mathvariant range it is sufficient to
     412        // multiply the position within the sequence order (multiplier) with the period of the sequence (which is constant for all number sequences)
     413        // and to add the character point of the first character within the number mathvariant range.
     414        // To this the baseChar calculated earlier is added to obtain the final code point.
     415        return baseChar + multiplier * (mathDoubleStruckZero - mathBoldDigitZero) + mathBoldDigitZero;
     416    }
     417    if (varType == Greekish) {
     418        switch (mathvariant) {
     419        case MathMLStyle::Bold:
     420            multiplier = 0;
     421            break;
     422        case MathMLStyle::Italic:
     423            multiplier = 1;
     424            break;
     425        case MathMLStyle::BoldItalic:
     426            multiplier = 2;
     427            break;
     428        case MathMLStyle::BoldSansSerif:
     429            multiplier = 3;
     430            break;
     431        case MathMLStyle::SansSerifBoldItalic:
     432            multiplier = 4;
     433            break;
     434        default:
     435            // This mathvariant isn't defined for Greek or is otherwise normal.
     436            return codePoint;
     437        }
     438        // See the Number case for an explanation of the following calculation.
     439        return baseChar + mathBoldUpperAlpha + multiplier * (mathItalicUpperAlpha - mathBoldUpperAlpha);
     440    }
     441
     442    UChar32 tempChar;
     443    UChar32 newChar;
     444    if (varType == Arabic) {
     445        // The Arabic mathematical block is not continuous, nor does it have a monotonic mapping to the unencoded characters, requiring the use of a lookup table.
     446        const MathVariantMapping* mapTable;
     447        size_t tableLength;
     448        switch (mathvariant) {
     449        case MathMLStyle::Initial:
     450            mapTable = arabicInitialMapTable;
     451            tableLength = WTF_ARRAY_LENGTH(arabicInitialMapTable);
     452            break;
     453        case MathMLStyle::Tailed:
     454            mapTable = arabicTailedMapTable;
     455            tableLength = WTF_ARRAY_LENGTH(arabicTailedMapTable);
     456            break;
     457        case MathMLStyle::Stretched:
     458            mapTable = arabicStretchedMapTable;
     459            tableLength = WTF_ARRAY_LENGTH(arabicStretchedMapTable);
     460            break;
     461        case MathMLStyle::Looped:
     462            mapTable = arabicLoopedMapTable;
     463            tableLength = WTF_ARRAY_LENGTH(arabicLoopedMapTable);
     464            break;
     465        case MathMLStyle::DoubleStruck:
     466            mapTable = arabicDoubleMapTable;
     467            tableLength = WTF_ARRAY_LENGTH(arabicDoubleMapTable);
     468            break;
     469        default:
     470            return codePoint; // No valid transformations exist.
     471        }
     472        newChar = MathVariantMappingSearch(codePoint, mapTable, tableLength);
     473    } else {
     474        // Must be Latin
     475        if (mathvariant > MathMLStyle::Monospace)
     476            return codePoint; // Latin doesn't support the Arabic mathvariants
     477        multiplier = mathvariant - 2;
     478        // This is possible because the values for NS_MATHML_MATHVARIANT_* are chosen to coincide with the order in which the encoded mathvariant characters are located within their unicode block (less an offset to avoid None and Normal variants)
     479        // See the Number case for an explanation of the following calculation
     480        tempChar = baseChar + mathBoldUpperA + multiplier * (mathItalicUpperA - mathBoldUpperA);
     481        // There are roughly twenty characters that are located outside of the mathematical block, so the spaces where they ought to be are used as keys for a lookup table containing the correct character mappings.
     482        newChar = MathVariantMappingSearch(tempChar, latinExceptionMapTable, WTF_ARRAY_LENGTH(latinExceptionMapTable));
     483    }
     484
     485    if (newChar)
     486        return newChar;
     487    if (varType == Latin)
     488        return tempChar;
     489    return codePoint; // This is an Arabic character without a corresponding mapping.
    88490}
    89491
     
    108510    ASSERT(m_mathVariantGlyphDirty);
    109511
    110     // This implements implicit italic mathvariant for single-char <mi>.
    111     // FIXME: Add full support for the mathvariant attribute (https://webkit.org/b/85735)
    112512    m_mathVariantGlyph = GlyphData();
    113513    m_mathVariantGlyphDirty = false;
     
    121521
    122522    const auto& tokenElement = element();
    123     if (tokenElement.hasTagName(MathMLNames::miTag) && !tokenElement.hasAttribute(mathvariantAttr)) {
    124         AtomicString textContent = element().textContent().stripWhiteSpace().simplifyWhiteSpace();
    125         if (textContent.length() == 1) {
    126             UChar32 codePoint = textContent[0];
    127             if (transformToItalic(codePoint))
    128                 m_mathVariantGlyph = style().fontCascade().glyphDataForCharacter(codePoint, !style().isLeftToRightDirection());
    129         }
     523    AtomicString textContent = element().textContent().stripWhiteSpace().simplifyWhiteSpace();
     524    if (textContent.length() == 1) {
     525        UChar32 codePoint = textContent[0];
     526        MathMLStyle::MathVariant mathvariant = mathMLStyle()->mathVariant();
     527        if (mathvariant == MathMLStyle::None)
     528            mathvariant = tokenElement.hasTagName(MathMLNames::miTag) ? MathMLStyle::Italic : MathMLStyle::Normal;
     529        UChar32 transformedCodePoint = mathVariant(codePoint, mathvariant);
     530        if (transformedCodePoint != codePoint)
     531            m_mathVariantGlyph = style().fontCascade().glyphDataForCharacter(transformedCodePoint, !style().isLeftToRightDirection());
    130532    }
    131533}
Note: See TracChangeset for help on using the changeset viewer.