1 /* 2 * Copyright (C) 2009 Alex Milowski (alex (at) milowski.com). All rights reserved. 3 * Copyright (C) 2010 Franois Sausset (sausset (at) 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 "RenderMathMLSquareRoot.h" 32 33 #include "GraphicsContext.h" 34 #include "MathMLNames.h" 35 #include "PaintInfo.h" 36 #include "Path.h" 37 38 namespace WebCore { 39 40 using namespace MathMLNames; 41 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.5f; 46 // Radical width (%) 47 const float gRadicalWidth = 0.75f; 48 // Horizontal position of the bottom point of the radical (%) 49 const float gRadicalBottomPointXPos= 0.5f; 50 // Horizontal position of the top left point of the radical (%) 51 const float gRadicalTopLeftPointXPos = 0.2f; 52 // Vertical position of the top left point of the radical (%) 53 const float gRadicalTopLeftPointYPos = 0.5f; 54 // Vertical shift of the left end point of the radical (%) 55 const float gRadicalLeftEndYShift = 0.05f; 56 // Additional bottom root padding (%) 57 const float gRootBottomPadding = 0.2f; 58 59 // Radical line thickness (%) 60 const float gRadicalLineThickness = 0.02f; 61 // Radical thick line thickness (%) 62 const float gRadicalThickLineThickness = 0.1f; 63 64 RenderMathMLSquareRoot::RenderMathMLSquareRoot(Node *expression) 65 : RenderMathMLBlock(expression) 66 { 67 } 68 69 void RenderMathMLSquareRoot::paint(PaintInfo& info, int tx, int ty) 70 { 71 RenderMathMLBlock::paint(info, tx, ty); 72 73 if (info.context->paintingDisabled()) 74 return; 75 76 tx += x(); 77 ty += y(); 78 79 int maxHeight = 0; 80 int width = 0; 81 RenderObject* current = firstChild(); 82 while (current) { 83 if (current->isBoxModelObject()) { 84 85 RenderBoxModelObject* box = toRenderBoxModelObject(current); 86 87 // Check to see if this box has a larger height 88 if (box->offsetHeight() > maxHeight) 89 maxHeight = box->offsetHeight(); 90 width += box->offsetWidth(); 91 } 92 current = current->nextSibling(); 93 } 94 // default to the font size in pixels if we're empty 95 if (!maxHeight) 96 maxHeight = style()->fontSize(); 97 98 int frontWidth = static_cast<int>(style()->fontSize() * gRadicalWidth); 99 int topStartShift = 0; 100 // Base height above which the shape of the root changes 101 int thresholdHeight = static_cast<int>(gThresholdBaseHeight * style()->fontSize()); 102 103 if (maxHeight > thresholdHeight && thresholdHeight) { 104 float shift = (maxHeight - thresholdHeight) / static_cast<float>(thresholdHeight); 105 if (shift > 1.) 106 shift = 1.0f; 107 topStartShift = static_cast<int>(gRadicalBottomPointXPos * frontWidth * shift); 108 } 109 110 width += topStartShift; 111 112 FloatPoint topStart(tx + frontWidth - topStartShift, ty); 113 FloatPoint bottomLeft(tx + frontWidth * gRadicalBottomPointXPos , ty + maxHeight + gRadicalBasePad); 114 FloatPoint topLeft(tx + frontWidth * gRadicalTopLeftPointXPos , ty + gRadicalTopLeftPointYPos * maxHeight); 115 FloatPoint leftEnd(tx , topLeft.y() + gRadicalLeftEndYShift * style()->fontSize()); 116 117 info.context->save(); 118 119 info.context->setStrokeThickness(gRadicalLineThickness * style()->fontSize()); 120 info.context->setStrokeStyle(SolidStroke); 121 info.context->setStrokeColor(style()->visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB); 122 info.context->setLineJoin(MiterJoin); 123 info.context->setMiterLimit(style()->fontSize()); 124 125 Path root; 126 127 root.moveTo(FloatPoint(topStart.x() + width , ty)); 128 // draw top 129 root.addLineTo(topStart); 130 // draw from top left corner to bottom point of radical 131 root.addLineTo(bottomLeft); 132 // draw from bottom point to top of left part of radical base "pocket" 133 root.addLineTo(topLeft); 134 // draw to end 135 root.addLineTo(leftEnd); 136 137 info.context->strokePath(root); 138 139 info.context->save(); 140 141 // Build a mask to draw the thick part of the root. 142 Path mask; 143 144 mask.moveTo(topStart); 145 mask.addLineTo(bottomLeft); 146 mask.addLineTo(topLeft); 147 mask.addLineTo(FloatPoint(2 * topLeft.x() - leftEnd.x(), 2 * topLeft.y() - leftEnd.y())); 148 149 info.context->clip(mask); 150 151 // Draw the thick part of the root. 152 info.context->setStrokeThickness(gRadicalThickLineThickness * style()->fontSize()); 153 info.context->setLineCap(SquareCap); 154 155 Path line; 156 line.moveTo(bottomLeft); 157 line.addLineTo(topLeft); 158 159 info.context->strokePath(line); 160 161 info.context->restore(); 162 163 info.context->restore(); 164 } 165 166 void RenderMathMLSquareRoot::layout() 167 { 168 int maxHeight = 0; 169 170 RenderObject* current = firstChild(); 171 while (current) { 172 if (current->isBoxModelObject()) { 173 RenderBoxModelObject* box = toRenderBoxModelObject(current); 174 175 if (box->offsetHeight() > maxHeight) 176 maxHeight = box->offsetHeight(); 177 178 box->style()->setVerticalAlign(BASELINE); 179 } 180 current = current->nextSibling(); 181 } 182 183 if (!maxHeight) 184 maxHeight = style()->fontSize(); 185 186 187 if (maxHeight > static_cast<int>(gThresholdBaseHeight * style()->fontSize())) 188 style()->setPaddingBottom(Length(static_cast<int>(gRootBottomPadding * style()->fontSize()), Fixed)); 189 190 191 RenderBlock::layout(); 192 } 193 194 } 195 196 #endif // ENABLE(MATHML) 197 198 199