1/* 2 * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> 4 * Copyright (C) 2007 Apple Inc. All rights reserved. 5 * Copyright (C) Research In Motion Limited 2011. All rights reserved. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 */ 22 23#include "config.h" 24#include "core/svg/SVGLengthContext.h" 25 26#include "SVGNames.h" 27#include "bindings/v8/ExceptionState.h" 28#include "core/css/CSSHelper.h" 29#include "core/dom/ExceptionCode.h" 30#include "core/rendering/RenderPart.h" 31#include "core/rendering/RenderView.h" 32#include "core/rendering/svg/RenderSVGRoot.h" 33#include "core/rendering/svg/RenderSVGViewportContainer.h" 34#include "core/svg/SVGSVGElement.h" 35#include "platform/fonts/FontMetrics.h" 36 37namespace WebCore { 38 39SVGLengthContext::SVGLengthContext(const SVGElement* context) 40 : m_context(context) 41{ 42} 43 44SVGLengthContext::SVGLengthContext(const SVGElement* context, const FloatRect& viewport) 45 : m_context(context) 46 , m_overridenViewport(viewport) 47{ 48} 49 50FloatRect SVGLengthContext::resolveRectangle(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const FloatRect& viewport, const SVGLength& x, const SVGLength& y, const SVGLength& width, const SVGLength& height) 51{ 52 ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN); 53 if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { 54 SVGLengthContext lengthContext(context); 55 return FloatRect(x.value(lengthContext), y.value(lengthContext), width.value(lengthContext), height.value(lengthContext)); 56 } 57 58 SVGLengthContext lengthContext(context, viewport); 59 return FloatRect(x.value(lengthContext) + viewport.x(), 60 y.value(lengthContext) + viewport.y(), 61 width.value(lengthContext), 62 height.value(lengthContext)); 63} 64 65FloatPoint SVGLengthContext::resolvePoint(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLength& x, const SVGLength& y) 66{ 67 ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN); 68 if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { 69 SVGLengthContext lengthContext(context); 70 return FloatPoint(x.value(lengthContext), y.value(lengthContext)); 71 } 72 73 // FIXME: valueAsPercentage() won't be correct for eg. cm units. They need to be resolved in user space and then be considered in objectBoundingBox space. 74 return FloatPoint(x.valueAsPercentage(), y.valueAsPercentage()); 75} 76 77float SVGLengthContext::resolveLength(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLength& x) 78{ 79 ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN); 80 if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { 81 SVGLengthContext lengthContext(context); 82 return x.value(lengthContext); 83 } 84 85 // FIXME: valueAsPercentage() won't be correct for eg. cm units. They need to be resolved in user space and then be considered in objectBoundingBox space. 86 return x.valueAsPercentage(); 87} 88 89float SVGLengthContext::convertValueToUserUnits(float value, SVGLengthMode mode, SVGLengthType fromUnit, ExceptionState& exceptionState) const 90{ 91 // If the SVGLengthContext carries a custom viewport, force resolving against it. 92 if (!m_overridenViewport.isEmpty()) { 93 // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed 94 if (fromUnit == LengthTypePercentage) 95 value /= 100; 96 return convertValueFromPercentageToUserUnits(value, mode, exceptionState); 97 } 98 99 switch (fromUnit) { 100 case LengthTypeUnknown: 101 exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError); 102 return 0; 103 case LengthTypeNumber: 104 return value; 105 case LengthTypePX: 106 return value; 107 case LengthTypePercentage: 108 return convertValueFromPercentageToUserUnits(value / 100, mode, exceptionState); 109 case LengthTypeEMS: 110 return convertValueFromEMSToUserUnits(value, exceptionState); 111 case LengthTypeEXS: 112 return convertValueFromEXSToUserUnits(value, exceptionState); 113 case LengthTypeCM: 114 return value * cssPixelsPerCentimeter; 115 case LengthTypeMM: 116 return value * cssPixelsPerMillimeter; 117 case LengthTypeIN: 118 return value * cssPixelsPerInch; 119 case LengthTypePT: 120 return value * cssPixelsPerPoint; 121 case LengthTypePC: 122 return value * cssPixelsPerPica; 123 } 124 125 ASSERT_NOT_REACHED(); 126 return 0; 127} 128 129float SVGLengthContext::convertValueFromUserUnits(float value, SVGLengthMode mode, SVGLengthType toUnit, ExceptionState& exceptionState) const 130{ 131 switch (toUnit) { 132 case LengthTypeUnknown: 133 exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError); 134 return 0; 135 case LengthTypeNumber: 136 return value; 137 case LengthTypePercentage: 138 return convertValueFromUserUnitsToPercentage(value * 100, mode, exceptionState); 139 case LengthTypeEMS: 140 return convertValueFromUserUnitsToEMS(value, exceptionState); 141 case LengthTypeEXS: 142 return convertValueFromUserUnitsToEXS(value, exceptionState); 143 case LengthTypePX: 144 return value; 145 case LengthTypeCM: 146 return value / cssPixelsPerCentimeter; 147 case LengthTypeMM: 148 return value / cssPixelsPerMillimeter; 149 case LengthTypeIN: 150 return value / cssPixelsPerInch; 151 case LengthTypePT: 152 return value / cssPixelsPerPoint; 153 case LengthTypePC: 154 return value / cssPixelsPerPica; 155 } 156 157 ASSERT_NOT_REACHED(); 158 return 0; 159} 160 161float SVGLengthContext::convertValueFromUserUnitsToPercentage(float value, SVGLengthMode mode, ExceptionState& exceptionState) const 162{ 163 FloatSize viewportSize; 164 if (!determineViewport(viewportSize)) { 165 exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError); 166 return 0; 167 } 168 169 switch (mode) { 170 case LengthModeWidth: 171 return value / viewportSize.width() * 100; 172 case LengthModeHeight: 173 return value / viewportSize.height() * 100; 174 case LengthModeOther: 175 return value / sqrtf(viewportSize.diagonalLengthSquared() / 2) * 100; 176 }; 177 178 ASSERT_NOT_REACHED(); 179 return 0; 180} 181 182float SVGLengthContext::convertValueFromPercentageToUserUnits(float value, SVGLengthMode mode, ExceptionState& exceptionState) const 183{ 184 FloatSize viewportSize; 185 if (!determineViewport(viewportSize)) { 186 exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError); 187 return 0; 188 } 189 190 switch (mode) { 191 case LengthModeWidth: 192 return value * viewportSize.width(); 193 case LengthModeHeight: 194 return value * viewportSize.height(); 195 case LengthModeOther: 196 return value * sqrtf(viewportSize.diagonalLengthSquared() / 2); 197 }; 198 199 ASSERT_NOT_REACHED(); 200 return 0; 201} 202 203static inline RenderStyle* renderStyleForLengthResolving(const SVGElement* context) 204{ 205 if (!context) 206 return 0; 207 208 const ContainerNode* currentContext = context; 209 while (currentContext) { 210 if (currentContext->renderer()) 211 return currentContext->renderer()->style(); 212 currentContext = currentContext->parentNode(); 213 } 214 215 // There must be at least a RenderSVGRoot renderer, carrying a style. 216 ASSERT_NOT_REACHED(); 217 return 0; 218} 219 220float SVGLengthContext::convertValueFromUserUnitsToEMS(float value, ExceptionState& exceptionState) const 221{ 222 RenderStyle* style = renderStyleForLengthResolving(m_context); 223 if (!style) { 224 exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError); 225 return 0; 226 } 227 228 float fontSize = style->specifiedFontSize(); 229 if (!fontSize) { 230 exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError); 231 return 0; 232 } 233 234 return value / fontSize; 235} 236 237float SVGLengthContext::convertValueFromEMSToUserUnits(float value, ExceptionState& exceptionState) const 238{ 239 RenderStyle* style = renderStyleForLengthResolving(m_context); 240 if (!style) { 241 exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError); 242 return 0; 243 } 244 245 return value * style->specifiedFontSize(); 246} 247 248float SVGLengthContext::convertValueFromUserUnitsToEXS(float value, ExceptionState& exceptionState) const 249{ 250 RenderStyle* style = renderStyleForLengthResolving(m_context); 251 if (!style) { 252 exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError); 253 return 0; 254 } 255 256 // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg 257 // if this causes problems in real world cases maybe it would be best to remove this 258 float xHeight = ceilf(style->fontMetrics().xHeight()); 259 if (!xHeight) { 260 exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError); 261 return 0; 262 } 263 264 return value / xHeight; 265} 266 267float SVGLengthContext::convertValueFromEXSToUserUnits(float value, ExceptionState& exceptionState) const 268{ 269 RenderStyle* style = renderStyleForLengthResolving(m_context); 270 if (!style) { 271 exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError); 272 return 0; 273 } 274 275 // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg 276 // if this causes problems in real world cases maybe it would be best to remove this 277 return value * ceilf(style->fontMetrics().xHeight()); 278} 279 280bool SVGLengthContext::determineViewport(FloatSize& viewportSize) const 281{ 282 if (!m_context) 283 return false; 284 285 // If an overriden viewport is given, it has precedence. 286 if (!m_overridenViewport.isEmpty()) { 287 viewportSize = m_overridenViewport.size(); 288 return true; 289 } 290 291 // Root <svg> element lengths are resolved against the top level viewport. 292 if (m_context->isOutermostSVGSVGElement()) { 293 viewportSize = toSVGSVGElement(m_context)->currentViewportSize(); 294 return true; 295 } 296 297 // Take size from nearest viewport element. 298 SVGElement* viewportElement = m_context->viewportElement(); 299 if (!viewportElement || !viewportElement->isSVGSVGElement()) 300 return false; 301 302 const SVGSVGElement* svg = toSVGSVGElement(viewportElement); 303 viewportSize = svg->currentViewBoxRect().size(); 304 if (viewportSize.isEmpty()) 305 viewportSize = svg->currentViewportSize(); 306 307 return true; 308} 309 310} 311