1 /* 2 * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis (at) 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 37 namespace WebCore { 38 39 SVGLengthContext::SVGLengthContext(const SVGElement* context) 40 : m_context(context) 41 { 42 } 43 44 SVGLengthContext::SVGLengthContext(const SVGElement* context, const FloatRect& viewport) 45 : m_context(context) 46 , m_overridenViewport(viewport) 47 { 48 } 49 50 FloatRect 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 65 FloatPoint 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 77 float 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 89 float 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 129 float 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 161 float 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 182 float 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 203 static 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 220 float 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 237 float 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 248 float 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 267 float 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 280 bool 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