1/* 2 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> 3 * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> 4 * Copyright (C) 2008 Apple Inc. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22#include "config.h" 23 24#if ENABLE(SVG_FONTS) 25#include "SVGFontFaceElement.h" 26 27#include "Attribute.h" 28#include "CSSFontFaceSrcValue.h" 29#include "CSSParser.h" 30#include "CSSPropertyNames.h" 31#include "CSSStyleSheet.h" 32#include "CSSValueKeywords.h" 33#include "CSSValueList.h" 34#include "Document.h" 35#include "ElementIterator.h" 36#include "Font.h" 37#include "SVGDocumentExtensions.h" 38#include "SVGFontElement.h" 39#include "SVGFontFaceSrcElement.h" 40#include "SVGGlyphElement.h" 41#include "SVGNames.h" 42#include "StyleProperties.h" 43#include "StyleResolver.h" 44#include "StyleRule.h" 45#include <math.h> 46 47namespace WebCore { 48 49using namespace SVGNames; 50 51inline SVGFontFaceElement::SVGFontFaceElement(const QualifiedName& tagName, Document& document) 52 : SVGElement(tagName, document) 53 , m_fontFaceRule(StyleRuleFontFace::create(MutableStyleProperties::create(CSSStrictMode))) 54 , m_fontElement(0) 55{ 56 ASSERT(hasTagName(font_faceTag)); 57} 58 59PassRefPtr<SVGFontFaceElement> SVGFontFaceElement::create(const QualifiedName& tagName, Document& document) 60{ 61 return adoptRef(new SVGFontFaceElement(tagName, document)); 62} 63 64void SVGFontFaceElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 65{ 66 CSSPropertyID propId = cssPropertyIdForSVGAttributeName(name); 67 if (propId > 0) { 68 m_fontFaceRule->mutableProperties().setProperty(propId, value, false); 69 rebuildFontFace(); 70 return; 71 } 72 73 SVGElement::parseAttribute(name, value); 74} 75 76unsigned SVGFontFaceElement::unitsPerEm() const 77{ 78 const AtomicString& value = fastGetAttribute(units_per_emAttr); 79 if (value.isEmpty()) 80 return gDefaultUnitsPerEm; 81 82 return static_cast<unsigned>(ceilf(value.toFloat())); 83} 84 85int SVGFontFaceElement::xHeight() const 86{ 87 return static_cast<int>(ceilf(fastGetAttribute(x_heightAttr).toFloat())); 88} 89 90float SVGFontFaceElement::horizontalOriginX() const 91{ 92 if (!m_fontElement) 93 return 0.0f; 94 95 // Spec: The X-coordinate in the font coordinate system of the origin of a glyph to be used when 96 // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.) 97 // If the attribute is not specified, the effect is as if a value of "0" were specified. 98 return m_fontElement->fastGetAttribute(horiz_origin_xAttr).toFloat(); 99} 100 101float SVGFontFaceElement::horizontalOriginY() const 102{ 103 if (!m_fontElement) 104 return 0.0f; 105 106 // Spec: The Y-coordinate in the font coordinate system of the origin of a glyph to be used when 107 // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.) 108 // If the attribute is not specified, the effect is as if a value of "0" were specified. 109 return m_fontElement->fastGetAttribute(horiz_origin_yAttr).toFloat(); 110} 111 112float SVGFontFaceElement::horizontalAdvanceX() const 113{ 114 if (!m_fontElement) 115 return 0.0f; 116 117 // Spec: The default horizontal advance after rendering a glyph in horizontal orientation. Glyph 118 // widths are required to be non-negative, even if the glyph is typically rendered right-to-left, 119 // as in Hebrew and Arabic scripts. 120 return m_fontElement->fastGetAttribute(horiz_adv_xAttr).toFloat(); 121} 122 123float SVGFontFaceElement::verticalOriginX() const 124{ 125 if (!m_fontElement) 126 return 0.0f; 127 128 // Spec: The default X-coordinate in the font coordinate system of the origin of a glyph to be used when 129 // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute 130 // were set to half of the effective value of attribute horiz-adv-x. 131 const AtomicString& value = m_fontElement->fastGetAttribute(vert_origin_xAttr); 132 if (value.isEmpty()) 133 return horizontalAdvanceX() / 2.0f; 134 135 return value.toFloat(); 136} 137 138float SVGFontFaceElement::verticalOriginY() const 139{ 140 if (!m_fontElement) 141 return 0.0f; 142 143 // Spec: The default Y-coordinate in the font coordinate system of the origin of a glyph to be used when 144 // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute 145 // were set to the position specified by the font's ascent attribute. 146 const AtomicString& value = m_fontElement->fastGetAttribute(vert_origin_yAttr); 147 if (value.isEmpty()) 148 return ascent(); 149 150 return value.toFloat(); 151} 152 153float SVGFontFaceElement::verticalAdvanceY() const 154{ 155 if (!m_fontElement) 156 return 0.0f; 157 158 // Spec: The default vertical advance after rendering a glyph in vertical orientation. If the attribute is 159 // not specified, the effect is as if a value equivalent of one em were specified (see units-per-em). 160 const AtomicString& value = m_fontElement->fastGetAttribute(vert_adv_yAttr); 161 if (value.isEmpty()) 162 return 1.0f; 163 164 return value.toFloat(); 165} 166 167int SVGFontFaceElement::ascent() const 168{ 169 // Spec: Same syntax and semantics as the 'ascent' descriptor within an @font-face rule. The maximum 170 // unaccented height of the font within the font coordinate system. If the attribute is not specified, 171 // the effect is as if the attribute were set to the difference between the units-per-em value and the 172 // vert-origin-y value for the corresponding font. 173 const AtomicString& ascentValue = fastGetAttribute(ascentAttr); 174 if (!ascentValue.isEmpty()) 175 return static_cast<int>(ceilf(ascentValue.toFloat())); 176 177 if (m_fontElement) { 178 const AtomicString& vertOriginY = m_fontElement->fastGetAttribute(vert_origin_yAttr); 179 if (!vertOriginY.isEmpty()) 180 return static_cast<int>(unitsPerEm()) - static_cast<int>(ceilf(vertOriginY.toFloat())); 181 } 182 183 // Match Batiks default value 184 return static_cast<int>(ceilf(unitsPerEm() * 0.8f)); 185} 186 187int SVGFontFaceElement::descent() const 188{ 189 // Spec: Same syntax and semantics as the 'descent' descriptor within an @font-face rule. The maximum 190 // unaccented depth of the font within the font coordinate system. If the attribute is not specified, 191 // the effect is as if the attribute were set to the vert-origin-y value for the corresponding font. 192 const AtomicString& descentValue = fastGetAttribute(descentAttr); 193 if (!descentValue.isEmpty()) { 194 // 14 different W3C SVG 1.1 testcases use a negative descent value, 195 // where a positive was meant to be used Including: 196 // animate-elem-24-t.svg, fonts-elem-01-t.svg, fonts-elem-02-t.svg (and 11 others) 197 int descent = static_cast<int>(ceilf(descentValue.toFloat())); 198 return descent < 0 ? -descent : descent; 199 } 200 201 if (m_fontElement) { 202 const AtomicString& vertOriginY = m_fontElement->fastGetAttribute(vert_origin_yAttr); 203 if (!vertOriginY.isEmpty()) 204 return static_cast<int>(ceilf(vertOriginY.toFloat())); 205 } 206 207 // Match Batiks default value 208 return static_cast<int>(ceilf(unitsPerEm() * 0.2f)); 209} 210 211String SVGFontFaceElement::fontFamily() const 212{ 213 return m_fontFaceRule->properties().getPropertyValue(CSSPropertyFontFamily); 214} 215 216SVGFontElement* SVGFontFaceElement::associatedFontElement() const 217{ 218 ASSERT(parentNode() == m_fontElement); 219 ASSERT(!parentNode() || isSVGFontElement(parentNode())); 220 return m_fontElement; 221} 222 223void SVGFontFaceElement::rebuildFontFace() 224{ 225 if (!inDocument()) { 226 ASSERT(!m_fontElement); 227 return; 228 } 229 230 // we currently ignore all but the first src element, alternatively we could concat them 231 auto srcElement = childrenOfType<SVGFontFaceSrcElement>(*this).first(); 232 233 bool describesParentFont = isSVGFontElement(parentNode()); 234 RefPtr<CSSValueList> list; 235 236 if (describesParentFont) { 237 m_fontElement = toSVGFontElement(parentNode()); 238 239 list = CSSValueList::createCommaSeparated(); 240 list->append(CSSFontFaceSrcValue::createLocal(fontFamily())); 241 } else { 242 m_fontElement = 0; 243 if (srcElement) 244 list = srcElement->srcValue(); 245 } 246 247 if (!list || !list->length()) 248 return; 249 250 // Parse in-memory CSS rules 251 m_fontFaceRule->mutableProperties().addParsedProperty(CSSProperty(CSSPropertySrc, list)); 252 253 if (describesParentFont) { 254 // Traverse parsed CSS values and associate CSSFontFaceSrcValue elements with ourselves. 255 RefPtr<CSSValue> src = m_fontFaceRule->properties().getPropertyCSSValue(CSSPropertySrc); 256 CSSValueList* srcList = toCSSValueList(src.get()); 257 258 unsigned srcLength = srcList ? srcList->length() : 0; 259 for (unsigned i = 0; i < srcLength; i++) { 260 if (CSSFontFaceSrcValue* item = toCSSFontFaceSrcValue(srcList->itemWithoutBoundsCheck(i))) 261 item->setSVGFontFaceElement(this); 262 } 263 } 264 265 document().styleResolverChanged(DeferRecalcStyle); 266} 267 268Node::InsertionNotificationRequest SVGFontFaceElement::insertedInto(ContainerNode& rootParent) 269{ 270 SVGElement::insertedInto(rootParent); 271 if (!rootParent.inDocument()) { 272 ASSERT(!m_fontElement); 273 return InsertionDone; 274 } 275 document().accessSVGExtensions()->registerSVGFontFaceElement(this); 276 277 rebuildFontFace(); 278 return InsertionDone; 279} 280 281void SVGFontFaceElement::removedFrom(ContainerNode& rootParent) 282{ 283 SVGElement::removedFrom(rootParent); 284 285 if (rootParent.inDocument()) { 286 m_fontElement = 0; 287 document().accessSVGExtensions()->unregisterSVGFontFaceElement(this); 288 m_fontFaceRule->mutableProperties().clear(); 289 290 document().styleResolverChanged(DeferRecalcStyle); 291 } else 292 ASSERT(!m_fontElement); 293} 294 295void SVGFontFaceElement::childrenChanged(const ChildChange& change) 296{ 297 SVGElement::childrenChanged(change); 298 rebuildFontFace(); 299} 300 301} // namespace WebCore 302 303#endif // ENABLE(SVG_FONTS) 304