1 /* 2 * Copyright (C) 2007 Eric Seidel <eric (at) webkit.org> 3 * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann (at) 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 "core/svg/SVGFontFaceElement.h" 26 27 #include <math.h> 28 #include "CSSPropertyNames.h" 29 #include "CSSValueKeywords.h" 30 #include "SVGNames.h" 31 #include "core/css/CSSFontFaceSrcValue.h" 32 #include "core/css/CSSParser.h" 33 #include "core/css/CSSStyleSheet.h" 34 #include "core/css/CSSValueList.h" 35 #include "core/css/StylePropertySet.h" 36 #include "core/css/StyleRule.h" 37 #include "core/dom/Attribute.h" 38 #include "core/dom/Document.h" 39 #include "core/platform/graphics/Font.h" 40 #include "core/svg/SVGDocumentExtensions.h" 41 #include "core/svg/SVGFontElement.h" 42 #include "core/svg/SVGFontFaceSrcElement.h" 43 #include "core/svg/SVGGlyphElement.h" 44 45 namespace WebCore { 46 47 using namespace SVGNames; 48 49 inline SVGFontFaceElement::SVGFontFaceElement(const QualifiedName& tagName, Document* document) 50 : SVGElement(tagName, document) 51 , m_fontFaceRule(StyleRuleFontFace::create()) 52 , m_fontElement(0) 53 { 54 ASSERT(hasTagName(font_faceTag)); 55 ScriptWrappable::init(this); 56 RefPtr<MutableStylePropertySet> styleDeclaration = MutableStylePropertySet::create(CSSStrictMode); 57 m_fontFaceRule->setProperties(styleDeclaration.release()); 58 } 59 60 PassRefPtr<SVGFontFaceElement> SVGFontFaceElement::create(const QualifiedName& tagName, Document* document) 61 { 62 return adoptRef(new SVGFontFaceElement(tagName, document)); 63 } 64 65 static CSSPropertyID cssPropertyIdForSVGAttributeName(const QualifiedName& attrName) 66 { 67 if (!attrName.namespaceURI().isNull()) 68 return CSSPropertyInvalid; 69 70 static HashMap<StringImpl*, CSSPropertyID>* propertyNameToIdMap = 0; 71 if (!propertyNameToIdMap) { 72 propertyNameToIdMap = new HashMap<StringImpl*, CSSPropertyID>; 73 // This is a list of all @font-face CSS properties which are exposed as SVG XML attributes 74 // Those commented out are not yet supported by WebCore's style system 75 // mapAttributeToCSSProperty(propertyNameToIdMap, accent_heightAttr); 76 // mapAttributeToCSSProperty(propertyNameToIdMap, alphabeticAttr); 77 // mapAttributeToCSSProperty(propertyNameToIdMap, ascentAttr); 78 // mapAttributeToCSSProperty(propertyNameToIdMap, bboxAttr); 79 // mapAttributeToCSSProperty(propertyNameToIdMap, cap_heightAttr); 80 // mapAttributeToCSSProperty(propertyNameToIdMap, descentAttr); 81 mapAttributeToCSSProperty(propertyNameToIdMap, font_familyAttr); 82 mapAttributeToCSSProperty(propertyNameToIdMap, font_sizeAttr); 83 mapAttributeToCSSProperty(propertyNameToIdMap, font_stretchAttr); 84 mapAttributeToCSSProperty(propertyNameToIdMap, font_styleAttr); 85 mapAttributeToCSSProperty(propertyNameToIdMap, font_variantAttr); 86 mapAttributeToCSSProperty(propertyNameToIdMap, font_weightAttr); 87 // mapAttributeToCSSProperty(propertyNameToIdMap, hangingAttr); 88 // mapAttributeToCSSProperty(propertyNameToIdMap, ideographicAttr); 89 // mapAttributeToCSSProperty(propertyNameToIdMap, mathematicalAttr); 90 // mapAttributeToCSSProperty(propertyNameToIdMap, overline_positionAttr); 91 // mapAttributeToCSSProperty(propertyNameToIdMap, overline_thicknessAttr); 92 // mapAttributeToCSSProperty(propertyNameToIdMap, panose_1Attr); 93 // mapAttributeToCSSProperty(propertyNameToIdMap, slopeAttr); 94 // mapAttributeToCSSProperty(propertyNameToIdMap, stemhAttr); 95 // mapAttributeToCSSProperty(propertyNameToIdMap, stemvAttr); 96 // mapAttributeToCSSProperty(propertyNameToIdMap, strikethrough_positionAttr); 97 // mapAttributeToCSSProperty(propertyNameToIdMap, strikethrough_thicknessAttr); 98 // mapAttributeToCSSProperty(propertyNameToIdMap, underline_positionAttr); 99 // mapAttributeToCSSProperty(propertyNameToIdMap, underline_thicknessAttr); 100 // mapAttributeToCSSProperty(propertyNameToIdMap, unicode_rangeAttr); 101 // mapAttributeToCSSProperty(propertyNameToIdMap, units_per_emAttr); 102 // mapAttributeToCSSProperty(propertyNameToIdMap, v_alphabeticAttr); 103 // mapAttributeToCSSProperty(propertyNameToIdMap, v_hangingAttr); 104 // mapAttributeToCSSProperty(propertyNameToIdMap, v_ideographicAttr); 105 // mapAttributeToCSSProperty(propertyNameToIdMap, v_mathematicalAttr); 106 // mapAttributeToCSSProperty(propertyNameToIdMap, widthsAttr); 107 // mapAttributeToCSSProperty(propertyNameToIdMap, x_heightAttr); 108 } 109 110 return propertyNameToIdMap->get(attrName.localName().impl()); 111 } 112 113 void SVGFontFaceElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 114 { 115 CSSPropertyID propId = cssPropertyIdForSVGAttributeName(name); 116 if (propId > 0) { 117 m_fontFaceRule->mutableProperties()->setProperty(propId, value, false); 118 rebuildFontFace(); 119 return; 120 } 121 122 SVGElement::parseAttribute(name, value); 123 } 124 125 unsigned SVGFontFaceElement::unitsPerEm() const 126 { 127 const AtomicString& value = fastGetAttribute(units_per_emAttr); 128 if (value.isEmpty()) 129 return gDefaultUnitsPerEm; 130 131 return static_cast<unsigned>(ceilf(value.toFloat())); 132 } 133 134 int SVGFontFaceElement::xHeight() const 135 { 136 return static_cast<int>(ceilf(fastGetAttribute(x_heightAttr).toFloat())); 137 } 138 139 float SVGFontFaceElement::horizontalOriginX() const 140 { 141 if (!m_fontElement) 142 return 0.0f; 143 144 // Spec: The X-coordinate in the font coordinate system of the origin of a glyph to be used when 145 // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.) 146 // If the attribute is not specified, the effect is as if a value of "0" were specified. 147 return m_fontElement->fastGetAttribute(horiz_origin_xAttr).toFloat(); 148 } 149 150 float SVGFontFaceElement::horizontalOriginY() const 151 { 152 if (!m_fontElement) 153 return 0.0f; 154 155 // Spec: The Y-coordinate in the font coordinate system of the origin of a glyph to be used when 156 // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.) 157 // If the attribute is not specified, the effect is as if a value of "0" were specified. 158 return m_fontElement->fastGetAttribute(horiz_origin_yAttr).toFloat(); 159 } 160 161 float SVGFontFaceElement::horizontalAdvanceX() const 162 { 163 if (!m_fontElement) 164 return 0.0f; 165 166 // Spec: The default horizontal advance after rendering a glyph in horizontal orientation. Glyph 167 // widths are required to be non-negative, even if the glyph is typically rendered right-to-left, 168 // as in Hebrew and Arabic scripts. 169 return m_fontElement->fastGetAttribute(horiz_adv_xAttr).toFloat(); 170 } 171 172 float SVGFontFaceElement::verticalOriginX() const 173 { 174 if (!m_fontElement) 175 return 0.0f; 176 177 // Spec: The default X-coordinate in the font coordinate system of the origin of a glyph to be used when 178 // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute 179 // were set to half of the effective value of attribute horiz-adv-x. 180 const AtomicString& value = m_fontElement->fastGetAttribute(vert_origin_xAttr); 181 if (value.isEmpty()) 182 return horizontalAdvanceX() / 2.0f; 183 184 return value.toFloat(); 185 } 186 187 float SVGFontFaceElement::verticalOriginY() const 188 { 189 if (!m_fontElement) 190 return 0.0f; 191 192 // Spec: The default Y-coordinate in the font coordinate system of the origin of a glyph to be used when 193 // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute 194 // were set to the position specified by the font's ascent attribute. 195 const AtomicString& value = m_fontElement->fastGetAttribute(vert_origin_yAttr); 196 if (value.isEmpty()) 197 return ascent(); 198 199 return value.toFloat(); 200 } 201 202 float SVGFontFaceElement::verticalAdvanceY() const 203 { 204 if (!m_fontElement) 205 return 0.0f; 206 207 // Spec: The default vertical advance after rendering a glyph in vertical orientation. If the attribute is 208 // not specified, the effect is as if a value equivalent of one em were specified (see units-per-em). 209 const AtomicString& value = m_fontElement->fastGetAttribute(vert_adv_yAttr); 210 if (value.isEmpty()) 211 return 1.0f; 212 213 return value.toFloat(); 214 } 215 216 int SVGFontFaceElement::ascent() const 217 { 218 // Spec: Same syntax and semantics as the 'ascent' descriptor within an @font-face rule. The maximum 219 // unaccented height of the font within the font coordinate system. If the attribute is not specified, 220 // the effect is as if the attribute were set to the difference between the units-per-em value and the 221 // vert-origin-y value for the corresponding font. 222 const AtomicString& ascentValue = fastGetAttribute(ascentAttr); 223 if (!ascentValue.isEmpty()) 224 return static_cast<int>(ceilf(ascentValue.toFloat())); 225 226 if (m_fontElement) { 227 const AtomicString& vertOriginY = m_fontElement->fastGetAttribute(vert_origin_yAttr); 228 if (!vertOriginY.isEmpty()) 229 return static_cast<int>(unitsPerEm()) - static_cast<int>(ceilf(vertOriginY.toFloat())); 230 } 231 232 // Match Batiks default value 233 return static_cast<int>(ceilf(unitsPerEm() * 0.8f)); 234 } 235 236 int SVGFontFaceElement::descent() const 237 { 238 // Spec: Same syntax and semantics as the 'descent' descriptor within an @font-face rule. The maximum 239 // unaccented depth of the font within the font coordinate system. If the attribute is not specified, 240 // the effect is as if the attribute were set to the vert-origin-y value for the corresponding font. 241 const AtomicString& descentValue = fastGetAttribute(descentAttr); 242 if (!descentValue.isEmpty()) { 243 // 14 different W3C SVG 1.1 testcases use a negative descent value, 244 // where a positive was meant to be used Including: 245 // animate-elem-24-t.svg, fonts-elem-01-t.svg, fonts-elem-02-t.svg (and 11 others) 246 int descent = static_cast<int>(ceilf(descentValue.toFloat())); 247 return descent < 0 ? -descent : descent; 248 } 249 250 if (m_fontElement) { 251 const AtomicString& vertOriginY = m_fontElement->fastGetAttribute(vert_origin_yAttr); 252 if (!vertOriginY.isEmpty()) 253 return static_cast<int>(ceilf(vertOriginY.toFloat())); 254 } 255 256 // Match Batiks default value 257 return static_cast<int>(ceilf(unitsPerEm() * 0.2f)); 258 } 259 260 String SVGFontFaceElement::fontFamily() const 261 { 262 return m_fontFaceRule->properties()->getPropertyValue(CSSPropertyFontFamily); 263 } 264 265 SVGFontElement* SVGFontFaceElement::associatedFontElement() const 266 { 267 ASSERT(parentNode() == m_fontElement); 268 ASSERT(!parentNode() || parentNode()->hasTagName(SVGNames::fontTag)); 269 return m_fontElement; 270 } 271 272 void SVGFontFaceElement::rebuildFontFace() 273 { 274 if (!inDocument()) { 275 ASSERT(!m_fontElement); 276 return; 277 } 278 279 // we currently ignore all but the first src element, alternatively we could concat them 280 SVGFontFaceSrcElement* srcElement = 0; 281 282 for (Node* child = firstChild(); child && !srcElement; child = child->nextSibling()) { 283 if (child->hasTagName(font_face_srcTag)) 284 srcElement = static_cast<SVGFontFaceSrcElement*>(child); 285 } 286 287 bool describesParentFont = parentNode()->hasTagName(SVGNames::fontTag); 288 RefPtr<CSSValueList> list; 289 290 if (describesParentFont) { 291 m_fontElement = toSVGFontElement(parentNode()); 292 293 list = CSSValueList::createCommaSeparated(); 294 list->append(CSSFontFaceSrcValue::createLocal(fontFamily())); 295 } else { 296 m_fontElement = 0; 297 if (srcElement) 298 list = srcElement->srcValue(); 299 } 300 301 if (!list || !list->length()) 302 return; 303 304 // Parse in-memory CSS rules 305 m_fontFaceRule->mutableProperties()->addParsedProperty(CSSProperty(CSSPropertySrc, list)); 306 307 if (describesParentFont) { 308 // Traverse parsed CSS values and associate CSSFontFaceSrcValue elements with ourselves. 309 RefPtr<CSSValue> src = m_fontFaceRule->properties()->getPropertyCSSValue(CSSPropertySrc); 310 CSSValueList* srcList = toCSSValueList(src.get()); 311 312 unsigned srcLength = srcList ? srcList->length() : 0; 313 for (unsigned i = 0; i < srcLength; i++) { 314 if (CSSFontFaceSrcValue* item = static_cast<CSSFontFaceSrcValue*>(srcList->itemWithoutBoundsCheck(i))) 315 item->setSVGFontFaceElement(this); 316 } 317 } 318 319 document()->styleResolverChanged(DeferRecalcStyle); 320 } 321 322 Node::InsertionNotificationRequest SVGFontFaceElement::insertedInto(ContainerNode* rootParent) 323 { 324 SVGElement::insertedInto(rootParent); 325 if (!rootParent->inDocument()) { 326 ASSERT(!m_fontElement); 327 return InsertionDone; 328 } 329 document()->accessSVGExtensions()->registerSVGFontFaceElement(this); 330 331 rebuildFontFace(); 332 return InsertionDone; 333 } 334 335 void SVGFontFaceElement::removedFrom(ContainerNode* rootParent) 336 { 337 SVGElement::removedFrom(rootParent); 338 339 if (rootParent->inDocument()) { 340 m_fontElement = 0; 341 document()->accessSVGExtensions()->unregisterSVGFontFaceElement(this); 342 m_fontFaceRule->mutableProperties()->clear(); 343 344 document()->styleResolverChanged(DeferRecalcStyle); 345 } else 346 ASSERT(!m_fontElement); 347 } 348 349 void SVGFontFaceElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) 350 { 351 SVGElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); 352 rebuildFontFace(); 353 } 354 355 } // namespace WebCore 356 357 #endif // ENABLE(SVG_FONTS) 358