WebCore/mathml/RenderMathMLRoot.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2009 Alex Milowski (alex@milowski.com). All rights reserved.
       
     3  * Copyright (C) 2010 François Sausset (sausset@gmail.com). All rights reserved.
       
     4  *
       
     5  * Redistribution and use in source and binary forms, with or without
       
     6  * modification, are permitted provided that the following conditions
       
     7  * are met:
       
     8  * 1. Redistributions of source code must retain the above copyright
       
     9  *    notice, this list of conditions and the following disclaimer.
       
    10  * 2. Redistributions in binary form must reproduce the above copyright
       
    11  *    notice, this list of conditions and the following disclaimer in the
       
    12  *    documentation and/or other materials provided with the distribution.
       
    13  *
       
    14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
    15  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
    16  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
    17  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
       
    18  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
       
    19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
       
    20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
       
    21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
       
    22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    25  */
       
    26 
       
    27 #include "config.h"
       
    28 
       
    29 #if ENABLE(MATHML)
       
    30 
       
    31 #include "RenderMathMLRoot.h"
       
    32 
       
    33 #include "GraphicsContext.h"
       
    34 #include "MathMLNames.h"
       
    35 
       
    36 namespace WebCore {
       
    37     
       
    38 using namespace MathMLNames;
       
    39 
       
    40 // Left margin of the radical (px)
       
    41 const int gRadicalLeftMargin = 3;
       
    42 // Bottom padding of the radical (px)
       
    43 const int gRadicalBasePad = 3;
       
    44 // Threshold above which the radical shape is modified to look nice with big bases (%)
       
    45 const float gThresholdBaseHeight = 1.5;
       
    46 // Radical width (%)
       
    47 const float gRadicalWidth = 0.75;
       
    48 // Horizontal position of the bottom point of the radical (%)
       
    49 const float gRadicalBottomPointXPos= 0.5;
       
    50 // Horizontal position of the top left point of the radical (%)
       
    51 const float gRadicalTopLeftPointXPos = 0.8;
       
    52 // Vertical position of the top left point of the radical (%)
       
    53 const float gRadicalTopLeftPointYPos = 0.625; 
       
    54 // Vertical shift of the left end point of the radical (%)
       
    55 const float gRadicalLeftEndYShift = 0.05;
       
    56 // Root padding around the base (%)
       
    57 const float gRootPadding = 0.2;
       
    58 // Additional bottom root padding (%)
       
    59 const float gRootBottomPadding = 0.2;
       
    60     
       
    61 // Radical line thickness (%)
       
    62 const float gRadicalLineThickness = 0.02;
       
    63 // Radical thick line thickness (%)
       
    64 const float gRadicalThickLineThickness = 0.1;
       
    65     
       
    66 RenderMathMLRoot::RenderMathMLRoot(Node *expression) 
       
    67 : RenderMathMLBlock(expression) 
       
    68 {
       
    69 }
       
    70 
       
    71 void RenderMathMLRoot::addChild(RenderObject* child, RenderObject* )
       
    72 {
       
    73     if (isEmpty()) {
       
    74         // Add a block for the index
       
    75         RenderBlock* block = new (renderArena()) RenderBlock(node());
       
    76         RefPtr<RenderStyle> indexStyle = makeBlockStyle();
       
    77         indexStyle->setDisplay(INLINE_BLOCK);
       
    78         block->setStyle(indexStyle.release());
       
    79         RenderBlock::addChild(block);
       
    80         
       
    81         // FIXME: the wrapping does not seem to be needed anymore.
       
    82         // this is the base, so wrap it so we can pad it
       
    83         block = new (renderArena()) RenderBlock(node());
       
    84         RefPtr<RenderStyle> baseStyle = makeBlockStyle();
       
    85         baseStyle->setDisplay(INLINE_BLOCK);
       
    86         baseStyle->setPaddingLeft(Length(5 * gRadicalWidth , Percent));
       
    87         block->setStyle(baseStyle.release());
       
    88         RenderBlock::addChild(block);
       
    89         block->addChild(child);
       
    90     } else {
       
    91         // always add to the index
       
    92         firstChild()->addChild(child);
       
    93     }
       
    94 }
       
    95     
       
    96 void RenderMathMLRoot::paint(PaintInfo& info, int tx, int ty)
       
    97 {
       
    98     RenderMathMLBlock::paint(info , tx , ty);
       
    99     
       
   100     if (info.context->paintingDisabled())
       
   101         return;
       
   102     
       
   103     tx += x();
       
   104     ty += y();
       
   105     
       
   106     RenderBoxModelObject* indexBox = toRenderBoxModelObject(lastChild());
       
   107     
       
   108     int maxHeight = indexBox->offsetHeight();
       
   109     // default to the font size in pixels if we're empty
       
   110     if (!maxHeight)
       
   111         maxHeight = style()->fontSize();
       
   112     int width = indexBox->offsetWidth();
       
   113     
       
   114     int indexWidth = 0;
       
   115     RenderObject* current = firstChild();
       
   116     while (current != lastChild()) {
       
   117         if (current->isBoxModelObject()) {
       
   118             RenderBoxModelObject* box = toRenderBoxModelObject(current);
       
   119             indexWidth += box->offsetWidth();
       
   120         }
       
   121         current = current->nextSibling();
       
   122     }
       
   123     
       
   124     int frontWidth = static_cast<int>(style()->fontSize() * gRadicalWidth);
       
   125     int topStartShift = 0;
       
   126     // Base height above which the shape of the root changes
       
   127     int thresholdHeight = static_cast<int>(gThresholdBaseHeight * style()->fontSize());
       
   128     
       
   129     if (maxHeight > thresholdHeight && thresholdHeight) {
       
   130         float shift = (maxHeight - thresholdHeight) / static_cast<float>(thresholdHeight);
       
   131         if (shift > 1.)
       
   132             shift = 1.;
       
   133         topStartShift = static_cast<int>(gRadicalBottomPointXPos * frontWidth * shift);
       
   134     }
       
   135     
       
   136     width += topStartShift;
       
   137     
       
   138     int rootPad = static_cast<int>(gRootPadding * style()->fontSize());
       
   139     int start = tx + indexWidth + gRadicalLeftMargin + style()->paddingLeft().value() - rootPad;
       
   140     ty += style()->paddingTop().value() - rootPad;
       
   141     
       
   142     FloatPoint topStart(start - topStartShift, ty);
       
   143     FloatPoint bottomLeft(start - gRadicalBottomPointXPos * frontWidth , ty + maxHeight + gRadicalBasePad);
       
   144     FloatPoint topLeft(start - gRadicalTopLeftPointXPos * frontWidth , ty + gRadicalTopLeftPointYPos * maxHeight);
       
   145     FloatPoint leftEnd(start - frontWidth , topLeft.y() + gRadicalLeftEndYShift * style()->fontSize());
       
   146     
       
   147     info.context->save();
       
   148     
       
   149     info.context->setStrokeThickness(gRadicalLineThickness * style()->fontSize());
       
   150     info.context->setStrokeStyle(SolidStroke);
       
   151     info.context->setStrokeColor(style()->visitedDependentColor(CSSPropertyColor), sRGBColorSpace);
       
   152     info.context->setLineJoin(MiterJoin);
       
   153     info.context->setMiterLimit(style()->fontSize());
       
   154     
       
   155     Path root;
       
   156     
       
   157     root.moveTo(FloatPoint(topStart.x() + width, ty));
       
   158     // draw top
       
   159     root.addLineTo(topStart);
       
   160     // draw from top left corner to bottom point of radical
       
   161     root.addLineTo(bottomLeft);
       
   162     // draw from bottom point to top of left part of radical base "pocket"
       
   163     root.addLineTo(topLeft);
       
   164     // draw to end
       
   165     root.addLineTo(leftEnd);
       
   166     
       
   167     info.context->beginPath();
       
   168     info.context->addPath(root);
       
   169     info.context->strokePath();
       
   170     
       
   171     info.context->save();
       
   172     
       
   173     // Build a mask to draw the thick part of the root.
       
   174     Path mask;
       
   175     
       
   176     mask.moveTo(topStart);
       
   177     mask.addLineTo(bottomLeft);
       
   178     mask.addLineTo(topLeft);
       
   179     mask.addLineTo(FloatPoint(2 * topLeft.x() - leftEnd.x(), 2 * topLeft.y() - leftEnd.y()));
       
   180     
       
   181     info.context->beginPath();
       
   182     info.context->addPath(mask);
       
   183     info.context->clip(mask);
       
   184     
       
   185     // Draw the thick part of the root.
       
   186     info.context->setStrokeThickness(gRadicalThickLineThickness * style()->fontSize());
       
   187     info.context->setLineCap(SquareCap);
       
   188     
       
   189     Path line;
       
   190     
       
   191     line = line.createLine(bottomLeft, topLeft);
       
   192     
       
   193     info.context->beginPath();
       
   194     info.context->addPath(line);
       
   195     info.context->strokePath();
       
   196     
       
   197     info.context->restore();
       
   198     
       
   199     info.context->restore();
       
   200 
       
   201 }
       
   202 
       
   203 void RenderMathMLRoot::layout()
       
   204 {
       
   205     RenderBlock::layout();
       
   206     
       
   207     int maxHeight = toRenderBoxModelObject(lastChild())->offsetHeight();
       
   208     
       
   209     RenderObject* current = lastChild()->firstChild();
       
   210     current->style()->setVerticalAlign(BASELINE);
       
   211     
       
   212     if (!maxHeight)
       
   213         maxHeight = style()->fontSize();
       
   214     
       
   215     // Base height above which the shape of the root changes
       
   216     int thresholdHeight = static_cast<int>(gThresholdBaseHeight * style()->fontSize());
       
   217     int topStartShift = 0;
       
   218     
       
   219     if (maxHeight > thresholdHeight && thresholdHeight) {
       
   220         float shift = (maxHeight - thresholdHeight) / static_cast<float>(thresholdHeight);
       
   221         if (shift > 1.)
       
   222             shift = 1.;
       
   223         int frontWidth = static_cast<int>(style()->fontSize() * gRadicalWidth);
       
   224         topStartShift = static_cast<int>(gRadicalBottomPointXPos * frontWidth * shift);
       
   225         
       
   226         style()->setPaddingBottom(Length(static_cast<int>(gRootBottomPadding * style()->fontSize()), Fixed));
       
   227     }
       
   228     
       
   229     // Positioning of the index
       
   230     RenderBoxModelObject* indexBox = toRenderBoxModelObject(firstChild()->firstChild());
       
   231     
       
   232     int indexShift = indexBox->offsetWidth() + topStartShift;
       
   233     int radicalHeight = static_cast<int>((1 - gRadicalTopLeftPointYPos) * maxHeight);
       
   234     int rootMarginTop = radicalHeight + style()->paddingBottom().value() + indexBox->offsetHeight() - (maxHeight + static_cast<int>(gRootPadding * style()->fontSize()));
       
   235     
       
   236     style()->setPaddingLeft(Length(indexShift, Fixed));
       
   237     if (rootMarginTop > 0)
       
   238         style()->setPaddingTop(Length(rootMarginTop + static_cast<int>(gRootPadding * style()->fontSize()), Fixed));
       
   239     
       
   240     setNeedsLayoutAndPrefWidthsRecalc();
       
   241     markContainingBlocksForLayout();
       
   242     RenderBlock::layout();
       
   243 
       
   244     indexBox->style()->setBottom(Length(radicalHeight + style()->paddingBottom().value(), Fixed));
       
   245 
       
   246     // Now that we've potentially changed its position, we need layout the index again.
       
   247     indexBox->setNeedsLayoutAndPrefWidthsRecalc();
       
   248     indexBox->layout();
       
   249 }
       
   250     
       
   251 }
       
   252 
       
   253 #endif // ENABLE(MATHML)
       
   254 
       
   255