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/page/Frame.h" 31 #include "core/platform/graphics/FontMetrics.h" 32 #include "core/rendering/RenderPart.h" 33 #include "core/rendering/RenderView.h" 34 #include "core/rendering/svg/RenderSVGRoot.h" 35 #include "core/rendering/svg/RenderSVGViewportContainer.h" 36 #include "core/svg/SVGSVGElement.h" 37 38 namespace WebCore { 39 40 SVGLengthContext::SVGLengthContext(const SVGElement* context) 41 : m_context(context) 42 { 43 } 44 45 SVGLengthContext::SVGLengthContext(const SVGElement* context, const FloatRect& viewport) 46 : m_context(context) 47 , m_overridenViewport(viewport) 48 { 49 } 50 51 FloatRect SVGLengthContext::resolveRectangle(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const FloatRect& viewport, const SVGLength& x, const SVGLength& y, const SVGLength& width, const SVGLength& height) 52 { 53 ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN); 54 if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { 55 SVGLengthContext lengthContext(context); 56 return FloatRect(x.value(lengthContext), y.value(lengthContext), width.value(lengthContext), height.value(lengthContext)); 57 } 58 59 SVGLengthContext lengthContext(context, viewport); 60 return FloatRect(x.value(lengthContext) + viewport.x(), 61 y.value(lengthContext) + viewport.y(), 62 width.value(lengthContext), 63 height.value(lengthContext)); 64 } 65 66 FloatPoint SVGLengthContext::resolvePoint(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLength& x, const SVGLength& y) 67 { 68 ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN); 69 if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { 70 SVGLengthContext lengthContext(context); 71 return FloatPoint(x.value(lengthContext), y.value(lengthContext)); 72 } 73 74 // 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. 75 return FloatPoint(x.valueAsPercentage(), y.valueAsPercentage()); 76 } 77 78 float SVGLengthContext::resolveLength(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLength& x) 79 { 80 ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN); 81 if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { 82 SVGLengthContext lengthContext(context); 83 return x.value(lengthContext); 84 } 85 86 // 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. 87 return x.valueAsPercentage(); 88 } 89 90 float SVGLengthContext::convertValueToUserUnits(float value, SVGLengthMode mode, SVGLengthType fromUnit, ExceptionState& es) const 91 { 92 // If the SVGLengthContext carries a custom viewport, force resolving against it. 93 if (!m_overridenViewport.isEmpty()) { 94 // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed 95 if (fromUnit == LengthTypePercentage) 96 value /= 100; 97 return convertValueFromPercentageToUserUnits(value, mode, es); 98 } 99 100 switch (fromUnit) { 101 case LengthTypeUnknown: 102 es.throwDOMException(NotSupportedError); 103 return 0; 104 case LengthTypeNumber: 105 return value; 106 case LengthTypePX: 107 return value; 108 case LengthTypePercentage: 109 return convertValueFromPercentageToUserUnits(value / 100, mode, es); 110 case LengthTypeEMS: 111 return convertValueFromEMSToUserUnits(value, es); 112 case LengthTypeEXS: 113 return convertValueFromEXSToUserUnits(value, es); 114 case LengthTypeCM: 115 return value * cssPixelsPerInch / 2.54f; 116 case LengthTypeMM: 117 return value * cssPixelsPerInch / 25.4f; 118 case LengthTypeIN: 119 return value * cssPixelsPerInch; 120 case LengthTypePT: 121 return value * cssPixelsPerInch / 72; 122 case LengthTypePC: 123 return value * cssPixelsPerInch / 6; 124 } 125 126 ASSERT_NOT_REACHED(); 127 return 0; 128 } 129 130 float SVGLengthContext::convertValueFromUserUnits(float value, SVGLengthMode mode, SVGLengthType toUnit, ExceptionState& es) const 131 { 132 switch (toUnit) { 133 case LengthTypeUnknown: 134 es.throwDOMException(NotSupportedError); 135 return 0; 136 case LengthTypeNumber: 137 return value; 138 case LengthTypePercentage: 139 return convertValueFromUserUnitsToPercentage(value * 100, mode, es); 140 case LengthTypeEMS: 141 return convertValueFromUserUnitsToEMS(value, es); 142 case LengthTypeEXS: 143 return convertValueFromUserUnitsToEXS(value, es); 144 case LengthTypePX: 145 return value; 146 case LengthTypeCM: 147 return value * 2.54f / cssPixelsPerInch; 148 case LengthTypeMM: 149 return value * 25.4f / cssPixelsPerInch; 150 case LengthTypeIN: 151 return value / cssPixelsPerInch; 152 case LengthTypePT: 153 return value * 72 / cssPixelsPerInch; 154 case LengthTypePC: 155 return value * 6 / cssPixelsPerInch; 156 } 157 158 ASSERT_NOT_REACHED(); 159 return 0; 160 } 161 162 float SVGLengthContext::convertValueFromUserUnitsToPercentage(float value, SVGLengthMode mode, ExceptionState& es) const 163 { 164 float width = 0; 165 float height = 0; 166 if (!determineViewport(width, height)) { 167 es.throwDOMException(NotSupportedError); 168 return 0; 169 } 170 171 switch (mode) { 172 case LengthModeWidth: 173 return value / width * 100; 174 case LengthModeHeight: 175 return value / height * 100; 176 case LengthModeOther: 177 return value / (sqrtf((width * width + height * height) / 2)) * 100; 178 }; 179 180 ASSERT_NOT_REACHED(); 181 return 0; 182 } 183 184 float SVGLengthContext::convertValueFromPercentageToUserUnits(float value, SVGLengthMode mode, ExceptionState& es) const 185 { 186 float width = 0; 187 float height = 0; 188 if (!determineViewport(width, height)) { 189 es.throwDOMException(NotSupportedError); 190 return 0; 191 } 192 193 switch (mode) { 194 case LengthModeWidth: 195 return value * width; 196 case LengthModeHeight: 197 return value * height; 198 case LengthModeOther: 199 return value * sqrtf((width * width + height * height) / 2); 200 }; 201 202 ASSERT_NOT_REACHED(); 203 return 0; 204 } 205 206 static inline RenderStyle* renderStyleForLengthResolving(const SVGElement* context) 207 { 208 if (!context) 209 return 0; 210 211 const ContainerNode* currentContext = context; 212 while (currentContext) { 213 if (currentContext->renderer()) 214 return currentContext->renderer()->style(); 215 currentContext = currentContext->parentNode(); 216 } 217 218 // There must be at least a RenderSVGRoot renderer, carrying a style. 219 ASSERT_NOT_REACHED(); 220 return 0; 221 } 222 223 float SVGLengthContext::convertValueFromUserUnitsToEMS(float value, ExceptionState& es) const 224 { 225 RenderStyle* style = renderStyleForLengthResolving(m_context); 226 if (!style) { 227 es.throwDOMException(NotSupportedError); 228 return 0; 229 } 230 231 float fontSize = style->specifiedFontSize(); 232 if (!fontSize) { 233 es.throwDOMException(NotSupportedError); 234 return 0; 235 } 236 237 return value / fontSize; 238 } 239 240 float SVGLengthContext::convertValueFromEMSToUserUnits(float value, ExceptionState& es) const 241 { 242 RenderStyle* style = renderStyleForLengthResolving(m_context); 243 if (!style) { 244 es.throwDOMException(NotSupportedError); 245 return 0; 246 } 247 248 return value * style->specifiedFontSize(); 249 } 250 251 float SVGLengthContext::convertValueFromUserUnitsToEXS(float value, ExceptionState& es) const 252 { 253 RenderStyle* style = renderStyleForLengthResolving(m_context); 254 if (!style) { 255 es.throwDOMException(NotSupportedError); 256 return 0; 257 } 258 259 // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg 260 // if this causes problems in real world cases maybe it would be best to remove this 261 float xHeight = ceilf(style->fontMetrics().xHeight()); 262 if (!xHeight) { 263 es.throwDOMException(NotSupportedError); 264 return 0; 265 } 266 267 return value / xHeight; 268 } 269 270 float SVGLengthContext::convertValueFromEXSToUserUnits(float value, ExceptionState& es) const 271 { 272 RenderStyle* style = renderStyleForLengthResolving(m_context); 273 if (!style) { 274 es.throwDOMException(NotSupportedError); 275 return 0; 276 } 277 278 // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg 279 // if this causes problems in real world cases maybe it would be best to remove this 280 return value * ceilf(style->fontMetrics().xHeight()); 281 } 282 283 bool SVGLengthContext::determineViewport(float& width, float& height) const 284 { 285 if (!m_context) 286 return false; 287 288 // If an overriden viewport is given, it has precedence. 289 if (!m_overridenViewport.isEmpty()) { 290 width = m_overridenViewport.width(); 291 height = m_overridenViewport.height(); 292 return true; 293 } 294 295 // SVGLengthContext should NEVER be used to resolve width/height values for <svg> elements, 296 // as they require special treatment, due the relationship with the CSS width/height properties. 297 ASSERT(m_context->document()->documentElement() != m_context); 298 299 // Take size from nearest viewport element. 300 SVGElement* viewportElement = m_context->viewportElement(); 301 if (!viewportElement || !viewportElement->isSVGSVGElement()) 302 return false; 303 304 const SVGSVGElement* svg = static_cast<const SVGSVGElement*>(viewportElement); 305 FloatSize viewportSize = svg->currentViewBoxRect().size(); 306 if (viewportSize.isEmpty()) 307 viewportSize = svg->currentViewportSize(); 308 309 width = viewportSize.width(); 310 height = viewportSize.height(); 311 return true; 312 } 313 314 } 315