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 "RenderMathMLRoot.h" 32 33 #include "GraphicsContext.h" 34 #include "MathMLNames.h" 35 #include "PaintInfo.h" 36 37 namespace WebCore { 38 39 using namespace MathMLNames; 40 41 // Left margin of the radical (px) 42 const int gRadicalLeftMargin = 3; 43 // Bottom padding of the radical (px) 44 const int gRadicalBasePad = 3; 45 // Threshold above which the radical shape is modified to look nice with big bases (%) 46 const float gThresholdBaseHeight = 1.5f; 47 // Radical width (%) 48 const float gRadicalWidth = 0.75f; 49 // Horizontal position of the bottom point of the radical (%) 50 const float gRadicalBottomPointXPos= 0.5f; 51 // Horizontal position of the top left point of the radical (%) 52 const float gRadicalTopLeftPointXPos = 0.8f; 53 // Vertical position of the top left point of the radical (%) 54 const float gRadicalTopLeftPointYPos = 0.625f; 55 // Vertical shift of the left end point of the radical (%) 56 const float gRadicalLeftEndYShift = 0.05f; 57 // Root padding around the base (%) 58 const float gRootPadding = 0.2f; 59 // Additional bottom root padding (%) 60 const float gRootBottomPadding = 0.2f; 61 62 // Radical line thickness (%) 63 const float gRadicalLineThickness = 0.02f; 64 // Radical thick line thickness (%) 65 const float gRadicalThickLineThickness = 0.1f; 66 67 RenderMathMLRoot::RenderMathMLRoot(Node *expression) 68 : RenderMathMLBlock(expression) 69 { 70 } 71 72 void RenderMathMLRoot::addChild(RenderObject* child, RenderObject* ) 73 { 74 if (isEmpty()) { 75 // Add a block for the index 76 RenderBlock* block = new (renderArena()) RenderBlock(node()); 77 RefPtr<RenderStyle> indexStyle = makeBlockStyle(); 78 indexStyle->setDisplay(INLINE_BLOCK); 79 block->setStyle(indexStyle.release()); 80 RenderBlock::addChild(block); 81 82 // FIXME: the wrapping does not seem to be needed anymore. 83 // this is the base, so wrap it so we can pad it 84 block = new (renderArena()) RenderBlock(node()); 85 RefPtr<RenderStyle> baseStyle = makeBlockStyle(); 86 baseStyle->setDisplay(INLINE_BLOCK); 87 baseStyle->setPaddingLeft(Length(5 * gRadicalWidth , Percent)); 88 block->setStyle(baseStyle.release()); 89 RenderBlock::addChild(block); 90 block->addChild(child); 91 } else { 92 // always add to the index 93 firstChild()->addChild(child); 94 } 95 } 96 97 void RenderMathMLRoot::paint(PaintInfo& info, int tx, int ty) 98 { 99 RenderMathMLBlock::paint(info , tx , ty); 100 101 if (info.context->paintingDisabled()) 102 return; 103 104 if (!firstChild() || !lastChild()) 105 return; 106 107 tx += x(); 108 ty += y(); 109 110 RenderBoxModelObject* indexBox = toRenderBoxModelObject(lastChild()); 111 112 int maxHeight = indexBox->offsetHeight(); 113 // default to the font size in pixels if we're empty 114 if (!maxHeight) 115 maxHeight = style()->fontSize(); 116 int width = indexBox->offsetWidth(); 117 118 int indexWidth = 0; 119 RenderObject* current = firstChild(); 120 while (current != lastChild()) { 121 if (current->isBoxModelObject()) { 122 RenderBoxModelObject* box = toRenderBoxModelObject(current); 123 indexWidth += box->offsetWidth(); 124 } 125 current = current->nextSibling(); 126 } 127 128 int frontWidth = static_cast<int>(style()->fontSize() * gRadicalWidth); 129 int topStartShift = 0; 130 // Base height above which the shape of the root changes 131 int thresholdHeight = static_cast<int>(gThresholdBaseHeight * style()->fontSize()); 132 133 if (maxHeight > thresholdHeight && thresholdHeight) { 134 float shift = (maxHeight - thresholdHeight) / static_cast<float>(thresholdHeight); 135 if (shift > 1.) 136 shift = 1.0f; 137 topStartShift = static_cast<int>(gRadicalBottomPointXPos * frontWidth * shift); 138 } 139 140 width += topStartShift; 141 142 int rootPad = static_cast<int>(gRootPadding * style()->fontSize()); 143 int start = tx + indexWidth + gRadicalLeftMargin + style()->paddingLeft().value() - rootPad; 144 ty += style()->paddingTop().value() - rootPad; 145 146 FloatPoint topStart(start - topStartShift, ty); 147 FloatPoint bottomLeft(start - gRadicalBottomPointXPos * frontWidth , ty + maxHeight + gRadicalBasePad); 148 FloatPoint topLeft(start - gRadicalTopLeftPointXPos * frontWidth , ty + gRadicalTopLeftPointYPos * maxHeight); 149 FloatPoint leftEnd(start - frontWidth , topLeft.y() + gRadicalLeftEndYShift * style()->fontSize()); 150 151 info.context->save(); 152 153 info.context->setStrokeThickness(gRadicalLineThickness * style()->fontSize()); 154 info.context->setStrokeStyle(SolidStroke); 155 info.context->setStrokeColor(style()->visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB); 156 info.context->setLineJoin(MiterJoin); 157 info.context->setMiterLimit(style()->fontSize()); 158 159 Path root; 160 161 root.moveTo(FloatPoint(topStart.x() + width, ty)); 162 // draw top 163 root.addLineTo(topStart); 164 // draw from top left corner to bottom point of radical 165 root.addLineTo(bottomLeft); 166 // draw from bottom point to top of left part of radical base "pocket" 167 root.addLineTo(topLeft); 168 // draw to end 169 root.addLineTo(leftEnd); 170 171 info.context->strokePath(root); 172 173 info.context->save(); 174 175 // Build a mask to draw the thick part of the root. 176 Path mask; 177 178 mask.moveTo(topStart); 179 mask.addLineTo(bottomLeft); 180 mask.addLineTo(topLeft); 181 mask.addLineTo(FloatPoint(2 * topLeft.x() - leftEnd.x(), 2 * topLeft.y() - leftEnd.y())); 182 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 line.moveTo(bottomLeft); 191 line.addLineTo(topLeft); 192 193 info.context->strokePath(line); 194 195 info.context->restore(); 196 197 info.context->restore(); 198 199 } 200 201 void RenderMathMLRoot::layout() 202 { 203 RenderBlock::layout(); 204 205 if (!firstChild() || !lastChild()) 206 return; 207 208 int maxHeight = toRenderBoxModelObject(lastChild())->offsetHeight(); 209 210 RenderObject* current = lastChild()->firstChild(); 211 if (current) 212 current->style()->setVerticalAlign(BASELINE); 213 214 if (!maxHeight) 215 maxHeight = style()->fontSize(); 216 217 // Base height above which the shape of the root changes 218 int thresholdHeight = static_cast<int>(gThresholdBaseHeight * style()->fontSize()); 219 int topStartShift = 0; 220 221 if (maxHeight > thresholdHeight && thresholdHeight) { 222 float shift = (maxHeight - thresholdHeight) / static_cast<float>(thresholdHeight); 223 if (shift > 1.) 224 shift = 1.0f; 225 int frontWidth = static_cast<int>(style()->fontSize() * gRadicalWidth); 226 topStartShift = static_cast<int>(gRadicalBottomPointXPos * frontWidth * shift); 227 228 style()->setPaddingBottom(Length(static_cast<int>(gRootBottomPadding * style()->fontSize()), Fixed)); 229 } 230 231 // Positioning of the index 232 RenderObject* possibleIndex = firstChild()->firstChild(); 233 while (possibleIndex && !possibleIndex->isBoxModelObject()) 234 possibleIndex = possibleIndex->nextSibling(); 235 RenderBoxModelObject* indexBox = toRenderBoxModelObject(possibleIndex); 236 if (!indexBox) 237 return; 238 239 int indexShift = indexBox->offsetWidth() + topStartShift; 240 int radicalHeight = static_cast<int>((1 - gRadicalTopLeftPointYPos) * maxHeight); 241 int rootMarginTop = radicalHeight + style()->paddingBottom().value() + indexBox->offsetHeight() - (maxHeight + static_cast<int>(gRootPadding * style()->fontSize())); 242 243 style()->setPaddingLeft(Length(indexShift, Fixed)); 244 if (rootMarginTop > 0) 245 style()->setPaddingTop(Length(rootMarginTop + static_cast<int>(gRootPadding * style()->fontSize()), Fixed)); 246 247 setNeedsLayout(true); 248 setPreferredLogicalWidthsDirty(true, false); 249 RenderBlock::layout(); 250 251 indexBox->style()->setBottom(Length(radicalHeight + style()->paddingBottom().value(), Fixed)); 252 253 // Now that we've potentially changed its position, we need layout the index again. 254 indexBox->setNeedsLayout(true); 255 indexBox->layout(); 256 } 257 258 } 259 260 #endif // ENABLE(MATHML) 261 262 263