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