/* Copyright (C) 2005 Apple Computer, Inc. Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> 2004, 2005, 2008 Rob Buis <buis@kde.org> Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> Based on khtml css code by: Copyright(C) 1999-2003 Lars Knoll(knoll@kde.org) (C) 2003 Apple Computer, Inc. (C) 2004 Allan Sandfeld Jensen(kde@carewolf.com) (C) 2004 Germain Garand(germain@ebooksfrance.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #if ENABLE(SVG) #include "CSSStyleSelector.h" #include "CSSPrimitiveValueMappings.h" #include "CSSPropertyNames.h" #include "CSSValueList.h" #include "Document.h" #include "ShadowValue.h" #include "SVGColor.h" #include "SVGNames.h" #include "SVGPaint.h" #include "SVGRenderStyle.h" #include "SVGRenderStyleDefs.h" #include "SVGStyledElement.h" #include "SVGURIReference.h" #include <stdlib.h> #include <wtf/MathExtras.h> #define HANDLE_INHERIT(prop, Prop) \ if (isInherit) \ { \ svgstyle->set##Prop(m_parentStyle->svgStyle()->prop()); \ return; \ } #define HANDLE_INHERIT_AND_INITIAL(prop, Prop) \ HANDLE_INHERIT(prop, Prop) \ if (isInitial) { \ svgstyle->set##Prop(SVGRenderStyle::initial##Prop()); \ return; \ } namespace WebCore { static float roundToNearestGlyphOrientationAngle(float angle) { angle = fabsf(fmodf(angle, 360.0f)); if (angle <= 45.0f || angle > 315.0f) return 0.0f; else if (angle > 45.0f && angle <= 135.0f) return 90.0f; else if (angle > 135.0f && angle <= 225.0f) return 180.0f; return 270.0f; } static int angleToGlyphOrientation(float angle) { angle = roundToNearestGlyphOrientationAngle(angle); if (angle == 0.0f) return GO_0DEG; else if (angle == 90.0f) return GO_90DEG; else if (angle == 180.0f) return GO_180DEG; else if (angle == 270.0f) return GO_270DEG; return -1; } static Color colorFromSVGColorCSSValue(SVGColor* svgColor, const Color& fgColor) { Color color; if (svgColor->colorType() == SVGColor::SVG_COLORTYPE_CURRENTCOLOR) color = fgColor; else color = svgColor->color(); return color; } void CSSStyleSelector::applySVGProperty(int id, CSSValue* value) { ASSERT(value); CSSPrimitiveValue* primitiveValue = 0; if (value->isPrimitiveValue()) primitiveValue = static_cast<CSSPrimitiveValue*>(value); SVGRenderStyle* svgstyle = m_style->accessSVGStyle(); unsigned short valueType = value->cssValueType(); bool isInherit = m_parentNode && valueType == CSSPrimitiveValue::CSS_INHERIT; bool isInitial = valueType == CSSPrimitiveValue::CSS_INITIAL || (!m_parentNode && valueType == CSSPrimitiveValue::CSS_INHERIT); // What follows is a list that maps the CSS properties into their // corresponding front-end RenderStyle values. Shorthands(e.g. border, // background) occur in this list as well and are only hit when mapping // "inherit" or "initial" into front-end values. switch (id) { // ident only properties case CSSPropertyAlignmentBaseline: { HANDLE_INHERIT_AND_INITIAL(alignmentBaseline, AlignmentBaseline) if (!primitiveValue) break; svgstyle->setAlignmentBaseline(*primitiveValue); break; } case CSSPropertyBaselineShift: { HANDLE_INHERIT_AND_INITIAL(baselineShift, BaselineShift); if (!primitiveValue) break; if (primitiveValue->getIdent()) { switch (primitiveValue->getIdent()) { case CSSValueBaseline: svgstyle->setBaselineShift(BS_BASELINE); break; case CSSValueSub: svgstyle->setBaselineShift(BS_SUB); break; case CSSValueSuper: svgstyle->setBaselineShift(BS_SUPER); break; default: break; } } else { svgstyle->setBaselineShift(BS_LENGTH); svgstyle->setBaselineShiftValue(SVGLength::fromCSSPrimitiveValue(primitiveValue)); } break; } case CSSPropertyKerning: { HANDLE_INHERIT_AND_INITIAL(kerning, Kerning); if (primitiveValue) svgstyle->setKerning(SVGLength::fromCSSPrimitiveValue(primitiveValue)); break; } case CSSPropertyDominantBaseline: { HANDLE_INHERIT_AND_INITIAL(dominantBaseline, DominantBaseline) if (primitiveValue) svgstyle->setDominantBaseline(*primitiveValue); break; } case CSSPropertyColorInterpolation: { HANDLE_INHERIT_AND_INITIAL(colorInterpolation, ColorInterpolation) if (primitiveValue) svgstyle->setColorInterpolation(*primitiveValue); break; } case CSSPropertyColorInterpolationFilters: { HANDLE_INHERIT_AND_INITIAL(colorInterpolationFilters, ColorInterpolationFilters) if (primitiveValue) svgstyle->setColorInterpolationFilters(*primitiveValue); break; } case CSSPropertyColorRendering: { HANDLE_INHERIT_AND_INITIAL(colorRendering, ColorRendering) if (primitiveValue) svgstyle->setColorRendering(*primitiveValue); break; } case CSSPropertyClipRule: { HANDLE_INHERIT_AND_INITIAL(clipRule, ClipRule) if (primitiveValue) svgstyle->setClipRule(*primitiveValue); break; } case CSSPropertyFillRule: { HANDLE_INHERIT_AND_INITIAL(fillRule, FillRule) if (primitiveValue) svgstyle->setFillRule(*primitiveValue); break; } case CSSPropertyStrokeLinejoin: { HANDLE_INHERIT_AND_INITIAL(joinStyle, JoinStyle) if (primitiveValue) svgstyle->setJoinStyle(*primitiveValue); break; } case CSSPropertyImageRendering: { HANDLE_INHERIT_AND_INITIAL(imageRendering, ImageRendering) if (primitiveValue) svgstyle->setImageRendering(*primitiveValue); break; } case CSSPropertyShapeRendering: { HANDLE_INHERIT_AND_INITIAL(shapeRendering, ShapeRendering) if (primitiveValue) svgstyle->setShapeRendering(*primitiveValue); break; } // end of ident only properties case CSSPropertyFill: { HANDLE_INHERIT_AND_INITIAL(fillPaint, FillPaint) if (value->isSVGPaint()) svgstyle->setFillPaint(static_cast<SVGPaint*>(value)); break; } case CSSPropertyStroke: { HANDLE_INHERIT_AND_INITIAL(strokePaint, StrokePaint) if (value->isSVGPaint()) svgstyle->setStrokePaint(static_cast<SVGPaint*>(value)); break; } case CSSPropertyStrokeWidth: { HANDLE_INHERIT_AND_INITIAL(strokeWidth, StrokeWidth) if (primitiveValue) svgstyle->setStrokeWidth(SVGLength::fromCSSPrimitiveValue(primitiveValue)); break; } case CSSPropertyStrokeDasharray: { HANDLE_INHERIT_AND_INITIAL(strokeDashArray, StrokeDashArray) if (!value->isValueList()) break; CSSValueList* dashes = static_cast<CSSValueList*>(value); Vector<SVGLength> array; size_t length = dashes->length(); for (size_t i = 0; i < length; ++i) { CSSValue* currValue = dashes->itemWithoutBoundsCheck(i); if (!currValue->isPrimitiveValue()) continue; CSSPrimitiveValue* dash = static_cast<CSSPrimitiveValue*>(dashes->itemWithoutBoundsCheck(i)); array.append(SVGLength::fromCSSPrimitiveValue(dash)); } svgstyle->setStrokeDashArray(array); break; } case CSSPropertyStrokeDashoffset: { HANDLE_INHERIT_AND_INITIAL(strokeDashOffset, StrokeDashOffset) if (primitiveValue) svgstyle->setStrokeDashOffset(SVGLength::fromCSSPrimitiveValue(primitiveValue)); break; } case CSSPropertyFillOpacity: { HANDLE_INHERIT_AND_INITIAL(fillOpacity, FillOpacity) if (!primitiveValue) return; float f = 0.0f; int type = primitiveValue->primitiveType(); if (type == CSSPrimitiveValue::CSS_PERCENTAGE) f = primitiveValue->getFloatValue() / 100.0f; else if (type == CSSPrimitiveValue::CSS_NUMBER) f = primitiveValue->getFloatValue(); else return; svgstyle->setFillOpacity(f); break; } case CSSPropertyStrokeOpacity: { HANDLE_INHERIT_AND_INITIAL(strokeOpacity, StrokeOpacity) if (!primitiveValue) return; float f = 0.0f; int type = primitiveValue->primitiveType(); if (type == CSSPrimitiveValue::CSS_PERCENTAGE) f = primitiveValue->getFloatValue() / 100.0f; else if (type == CSSPrimitiveValue::CSS_NUMBER) f = primitiveValue->getFloatValue(); else return; svgstyle->setStrokeOpacity(f); break; } case CSSPropertyStopOpacity: { HANDLE_INHERIT_AND_INITIAL(stopOpacity, StopOpacity) if (!primitiveValue) return; float f = 0.0f; int type = primitiveValue->primitiveType(); if (type == CSSPrimitiveValue::CSS_PERCENTAGE) f = primitiveValue->getFloatValue() / 100.0f; else if (type == CSSPrimitiveValue::CSS_NUMBER) f = primitiveValue->getFloatValue(); else return; svgstyle->setStopOpacity(f); break; } case CSSPropertyMarkerStart: { HANDLE_INHERIT_AND_INITIAL(markerStartResource, MarkerStartResource) if (!primitiveValue) return; String s; int type = primitiveValue->primitiveType(); if (type == CSSPrimitiveValue::CSS_URI) s = primitiveValue->getStringValue(); else return; svgstyle->setMarkerStartResource(SVGURIReference::getTarget(s)); break; } case CSSPropertyMarkerMid: { HANDLE_INHERIT_AND_INITIAL(markerMidResource, MarkerMidResource) if (!primitiveValue) return; String s; int type = primitiveValue->primitiveType(); if (type == CSSPrimitiveValue::CSS_URI) s = primitiveValue->getStringValue(); else return; svgstyle->setMarkerMidResource(SVGURIReference::getTarget(s)); break; } case CSSPropertyMarkerEnd: { HANDLE_INHERIT_AND_INITIAL(markerEndResource, MarkerEndResource) if (!primitiveValue) return; String s; int type = primitiveValue->primitiveType(); if (type == CSSPrimitiveValue::CSS_URI) s = primitiveValue->getStringValue(); else return; svgstyle->setMarkerEndResource(SVGURIReference::getTarget(s)); break; } case CSSPropertyStrokeLinecap: { HANDLE_INHERIT_AND_INITIAL(capStyle, CapStyle) if (primitiveValue) svgstyle->setCapStyle(*primitiveValue); break; } case CSSPropertyStrokeMiterlimit: { HANDLE_INHERIT_AND_INITIAL(strokeMiterLimit, StrokeMiterLimit) if (!primitiveValue) return; float f = 0.0f; int type = primitiveValue->primitiveType(); if (type == CSSPrimitiveValue::CSS_NUMBER) f = primitiveValue->getFloatValue(); else return; svgstyle->setStrokeMiterLimit(f); break; } case CSSPropertyFilter: { HANDLE_INHERIT_AND_INITIAL(filterResource, FilterResource) if (!primitiveValue) return; String s; int type = primitiveValue->primitiveType(); if (type == CSSPrimitiveValue::CSS_URI) s = primitiveValue->getStringValue(); else return; svgstyle->setFilterResource(SVGURIReference::getTarget(s)); break; } case CSSPropertyMask: { HANDLE_INHERIT_AND_INITIAL(maskerResource, MaskerResource) if (!primitiveValue) return; String s; int type = primitiveValue->primitiveType(); if (type == CSSPrimitiveValue::CSS_URI) s = primitiveValue->getStringValue(); else return; svgstyle->setMaskerResource(SVGURIReference::getTarget(s)); break; } case CSSPropertyClipPath: { HANDLE_INHERIT_AND_INITIAL(clipperResource, ClipperResource) if (!primitiveValue) return; String s; int type = primitiveValue->primitiveType(); if (type == CSSPrimitiveValue::CSS_URI) s = primitiveValue->getStringValue(); else return; svgstyle->setClipperResource(SVGURIReference::getTarget(s)); break; } case CSSPropertyTextAnchor: { HANDLE_INHERIT_AND_INITIAL(textAnchor, TextAnchor) if (primitiveValue) svgstyle->setTextAnchor(*primitiveValue); break; } case CSSPropertyWritingMode: { HANDLE_INHERIT_AND_INITIAL(writingMode, WritingMode) if (primitiveValue) svgstyle->setWritingMode(*primitiveValue); break; } case CSSPropertyStopColor: { HANDLE_INHERIT_AND_INITIAL(stopColor, StopColor); if (value->isSVGColor()) svgstyle->setStopColor(colorFromSVGColorCSSValue(static_cast<SVGColor*>(value), m_style->color())); break; } case CSSPropertyLightingColor: { HANDLE_INHERIT_AND_INITIAL(lightingColor, LightingColor); if (value->isSVGColor()) svgstyle->setLightingColor(colorFromSVGColorCSSValue(static_cast<SVGColor*>(value), m_style->color())); break; } case CSSPropertyFloodOpacity: { HANDLE_INHERIT_AND_INITIAL(floodOpacity, FloodOpacity) if (!primitiveValue) return; float f = 0.0f; int type = primitiveValue->primitiveType(); if (type == CSSPrimitiveValue::CSS_PERCENTAGE) f = primitiveValue->getFloatValue() / 100.0f; else if (type == CSSPrimitiveValue::CSS_NUMBER) f = primitiveValue->getFloatValue(); else return; svgstyle->setFloodOpacity(f); break; } case CSSPropertyFloodColor: { HANDLE_INHERIT_AND_INITIAL(floodColor, FloodColor); if (value->isSVGColor()) svgstyle->setFloodColor(colorFromSVGColorCSSValue(static_cast<SVGColor*>(value), m_style->color())); break; } case CSSPropertyGlyphOrientationHorizontal: { HANDLE_INHERIT_AND_INITIAL(glyphOrientationHorizontal, GlyphOrientationHorizontal) if (!primitiveValue) return; if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_DEG) { int orientation = angleToGlyphOrientation(primitiveValue->getFloatValue()); ASSERT(orientation != -1); svgstyle->setGlyphOrientationHorizontal((EGlyphOrientation) orientation); } break; } case CSSPropertyGlyphOrientationVertical: { HANDLE_INHERIT_AND_INITIAL(glyphOrientationVertical, GlyphOrientationVertical) if (!primitiveValue) return; if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_DEG) { int orientation = angleToGlyphOrientation(primitiveValue->getFloatValue()); ASSERT(orientation != -1); svgstyle->setGlyphOrientationVertical((EGlyphOrientation) orientation); } else if (primitiveValue->getIdent() == CSSValueAuto) svgstyle->setGlyphOrientationVertical(GO_AUTO); break; } case CSSPropertyEnableBackground: // Silently ignoring this property for now // http://bugs.webkit.org/show_bug.cgi?id=6022 break; case CSSPropertyWebkitSvgShadow: { if (isInherit) return svgstyle->setShadow(m_parentStyle->svgStyle()->shadow() ? new ShadowData(*m_parentStyle->svgStyle()->shadow()) : 0); if (isInitial || primitiveValue) // initial | none return svgstyle->setShadow(0); if (!value->isValueList()) return; CSSValueList *list = static_cast<CSSValueList*>(value); if (!list->length()) return; CSSValue* firstValue = list->itemWithoutBoundsCheck(0); if (!firstValue->isShadowValue()) return; ShadowValue* item = static_cast<ShadowValue*>(firstValue); int x = item->x->computeLengthInt(style(), m_rootElementStyle); int y = item->y->computeLengthInt(style(), m_rootElementStyle); int blur = item->blur ? item->blur->computeLengthInt(style(), m_rootElementStyle) : 0; Color color; if (item->color) color = getColorFromPrimitiveValue(item->color.get()); // -webkit-svg-shadow does should not have a spread or style ASSERT(!item->spread); ASSERT(!item->style); ShadowData* shadowData = new ShadowData(x, y, blur, 0, Normal, false, color.isValid() ? color : Color::transparent); svgstyle->setShadow(shadowData); return; } case CSSPropertyVectorEffect: { HANDLE_INHERIT_AND_INITIAL(vectorEffect, VectorEffect) if (!primitiveValue) break; svgstyle->setVectorEffect(*primitiveValue); break; } default: // If you crash here, it's because you added a css property and are not handling it // in either this switch statement or the one in CSSStyleSelector::applyProperty ASSERT_WITH_MESSAGE(0, "unimplemented propertyID: %d", id); return; } } } #endif