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 "SVGFontFaceElement.h" 26 27 #include "CString.h" 28 #include "CSSFontFaceRule.h" 29 #include "CSSFontFaceSrcValue.h" 30 #include "CSSParser.h" 31 #include "CSSProperty.h" 32 #include "CSSPropertyNames.h" 33 #include "CSSStyleSelector.h" 34 #include "CSSStyleSheet.h" 35 #include "CSSValueKeywords.h" 36 #include "CSSValueList.h" 37 #include "Document.h" 38 #include "Font.h" 39 #include "MappedAttribute.h" 40 #include "SVGFontElement.h" 41 #include "SVGFontFaceSrcElement.h" 42 #include "SVGGlyphElement.h" 43 #include "SVGNames.h" 44 #include <math.h> 45 46 namespace WebCore { 47 48 using namespace SVGNames; 49 50 SVGFontFaceElement::SVGFontFaceElement(const QualifiedName& tagName, Document* doc) 51 : SVGElement(tagName, doc) 52 , m_fontFaceRule(CSSFontFaceRule::create()) 53 , m_styleDeclaration(CSSMutableStyleDeclaration::create()) 54 { 55 m_styleDeclaration->setParent(document()->mappedElementSheet()); 56 m_styleDeclaration->setStrictParsing(true); 57 m_fontFaceRule->setDeclaration(m_styleDeclaration.get()); 58 } 59 60 SVGFontFaceElement::~SVGFontFaceElement() 61 { 62 removeFromMappedElementSheet(); 63 } 64 65 static int cssPropertyIdForSVGAttributeName(const QualifiedName& attrName) 66 { 67 if (!attrName.namespaceURI().isNull()) 68 return 0; 69 70 static HashMap<AtomicStringImpl*, int>* propertyNameToIdMap = 0; 71 if (!propertyNameToIdMap) { 72 propertyNameToIdMap = new HashMap<AtomicStringImpl*, int>; 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::parseMappedAttribute(MappedAttribute* attr) 114 { 115 int propId = cssPropertyIdForSVGAttributeName(attr->name()); 116 if (propId > 0) { 117 m_styleDeclaration->setProperty(propId, attr->value(), false); 118 if (inDocument()) 119 rebuildFontFace(); 120 return; 121 } 122 123 SVGElement::parseMappedAttribute(attr); 124 } 125 126 unsigned SVGFontFaceElement::unitsPerEm() const 127 { 128 const AtomicString& value = getAttribute(units_per_emAttr); 129 if (value.isEmpty()) 130 return defaultUnitsPerEm; 131 132 return static_cast<unsigned>(ceilf(value.toFloat())); 133 } 134 135 int SVGFontFaceElement::xHeight() const 136 { 137 return static_cast<int>(ceilf(getAttribute(x_heightAttr).toFloat())); 138 } 139 140 float SVGFontFaceElement::horizontalOriginX() const 141 { 142 if (!m_fontElement) 143 return 0.0f; 144 145 // Spec: The X-coordinate in the font coordinate system of the origin of a glyph to be used when 146 // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.) 147 // If the attribute is not specified, the effect is as if a value of "0" were specified. 148 return m_fontElement->getAttribute(horiz_origin_xAttr).toFloat(); 149 } 150 151 float SVGFontFaceElement::horizontalOriginY() const 152 { 153 if (!m_fontElement) 154 return 0.0f; 155 156 // Spec: The Y-coordinate in the font coordinate system of the origin of a glyph to be used when 157 // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.) 158 // If the attribute is not specified, the effect is as if a value of "0" were specified. 159 return m_fontElement->getAttribute(horiz_origin_yAttr).toFloat(); 160 } 161 162 float SVGFontFaceElement::horizontalAdvanceX() const 163 { 164 if (!m_fontElement) 165 return 0.0f; 166 167 // Spec: The default horizontal advance after rendering a glyph in horizontal orientation. Glyph 168 // widths are required to be non-negative, even if the glyph is typically rendered right-to-left, 169 // as in Hebrew and Arabic scripts. 170 return m_fontElement->getAttribute(horiz_adv_xAttr).toFloat(); 171 } 172 173 float SVGFontFaceElement::verticalOriginX() const 174 { 175 if (!m_fontElement) 176 return 0.0f; 177 178 // Spec: The default X-coordinate in the font coordinate system of the origin of a glyph to be used when 179 // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute 180 // were set to half of the effective value of attribute horiz-adv-x. 181 const AtomicString& value = m_fontElement->getAttribute(vert_origin_xAttr); 182 if (value.isEmpty()) 183 return horizontalAdvanceX() / 2.0f; 184 185 return value.toFloat(); 186 } 187 188 float SVGFontFaceElement::verticalOriginY() const 189 { 190 if (!m_fontElement) 191 return 0.0f; 192 193 // Spec: The default Y-coordinate in the font coordinate system of the origin of a glyph to be used when 194 // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute 195 // were set to the position specified by the font's ascent attribute. 196 const AtomicString& value = m_fontElement->getAttribute(vert_origin_yAttr); 197 if (value.isEmpty()) 198 return ascent(); 199 200 return value.toFloat(); 201 } 202 203 float SVGFontFaceElement::verticalAdvanceY() const 204 { 205 if (!m_fontElement) 206 return 0.0f; 207 208 // Spec: The default vertical advance after rendering a glyph in vertical orientation. If the attribute is 209 // not specified, the effect is as if a value equivalent of one em were specified (see units-per-em). 210 const AtomicString& value = m_fontElement->getAttribute(vert_adv_yAttr); 211 if (value.isEmpty()) 212 return 1.0f; 213 214 return value.toFloat(); 215 } 216 217 int SVGFontFaceElement::ascent() const 218 { 219 // Spec: Same syntax and semantics as the 'ascent' descriptor within an @font-face rule. The maximum 220 // unaccented height of the font within the font coordinate system. If the attribute is not specified, 221 // the effect is as if the attribute were set to the difference between the units-per-em value and the 222 // vert-origin-y value for the corresponding font. 223 const AtomicString& ascentValue = getAttribute(ascentAttr); 224 if (!ascentValue.isEmpty()) 225 return static_cast<int>(ceilf(ascentValue.toFloat())); 226 227 if (m_fontElement) { 228 const AtomicString& vertOriginY = m_fontElement->getAttribute(vert_origin_yAttr); 229 if (!vertOriginY.isEmpty()) 230 return static_cast<int>(unitsPerEm()) - static_cast<int>(ceilf(vertOriginY.toFloat())); 231 } 232 233 // Match Batiks default value 234 return static_cast<int>(ceilf(unitsPerEm() * 0.8f)); 235 } 236 237 int SVGFontFaceElement::descent() const 238 { 239 // Spec: Same syntax and semantics as the 'descent' descriptor within an @font-face rule. The maximum 240 // unaccented depth of the font within the font coordinate system. If the attribute is not specified, 241 // the effect is as if the attribute were set to the vert-origin-y value for the corresponding font. 242 const AtomicString& descentValue = getAttribute(descentAttr); 243 if (!descentValue.isEmpty()) { 244 // 14 different W3C SVG 1.1 testcases use a negative descent value, 245 // where a positive was meant to be used Including: 246 // animate-elem-24-t.svg, fonts-elem-01-t.svg, fonts-elem-02-t.svg (and 11 others) 247 int descent = static_cast<int>(ceilf(descentValue.toFloat())); 248 return descent < 0 ? -descent : descent; 249 } 250 251 if (m_fontElement) { 252 const AtomicString& vertOriginY = m_fontElement->getAttribute(vert_origin_yAttr); 253 if (!vertOriginY.isEmpty()) 254 return static_cast<int>(ceilf(vertOriginY.toFloat())); 255 } 256 257 // Match Batiks default value 258 return static_cast<int>(ceilf(unitsPerEm() * 0.2f)); 259 } 260 261 String SVGFontFaceElement::fontFamily() const 262 { 263 return m_styleDeclaration->getPropertyValue(CSSPropertyFontFamily); 264 } 265 266 void SVGFontFaceElement::rebuildFontFace() 267 { 268 ASSERT(inDocument()); 269 270 // we currently ignore all but the first src element, alternatively we could concat them 271 SVGFontFaceSrcElement* srcElement = 0; 272 273 for (Node* child = firstChild(); child && !srcElement; child = child->nextSibling()) { 274 if (child->hasTagName(font_face_srcTag)) 275 srcElement = static_cast<SVGFontFaceSrcElement*>(child); 276 } 277 278 bool describesParentFont = parentNode()->hasTagName(SVGNames::fontTag); 279 RefPtr<CSSValueList> list; 280 281 if (describesParentFont) { 282 m_fontElement = static_cast<SVGFontElement*>(parentNode()); 283 284 list = CSSValueList::createCommaSeparated(); 285 list->append(CSSFontFaceSrcValue::createLocal(fontFamily())); 286 } else { 287 m_fontElement = 0; 288 if (srcElement) 289 list = srcElement->srcValue(); 290 } 291 292 if (!list) 293 return; 294 295 // Parse in-memory CSS rules 296 CSSProperty srcProperty(CSSPropertySrc, list); 297 const CSSProperty* srcPropertyRef = &srcProperty; 298 m_styleDeclaration->addParsedProperties(&srcPropertyRef, 1); 299 300 if (describesParentFont) { 301 // Traverse parsed CSS values and associate CSSFontFaceSrcValue elements with ourselves. 302 RefPtr<CSSValue> src = m_styleDeclaration->getPropertyCSSValue(CSSPropertySrc); 303 CSSValueList* srcList = static_cast<CSSValueList*>(src.get()); 304 305 unsigned srcLength = srcList ? srcList->length() : 0; 306 for (unsigned i = 0; i < srcLength; i++) { 307 if (CSSFontFaceSrcValue* item = static_cast<CSSFontFaceSrcValue*>(srcList->itemWithoutBoundsCheck(i))) 308 item->setSVGFontFaceElement(this); 309 } 310 } 311 312 document()->updateStyleSelector(); 313 } 314 315 void SVGFontFaceElement::insertedIntoDocument() 316 { 317 SVGElement::insertedIntoDocument(); 318 document()->mappedElementSheet()->append(m_fontFaceRule); 319 rebuildFontFace(); 320 } 321 322 void SVGFontFaceElement::removedFromDocument() 323 { 324 removeFromMappedElementSheet(); 325 SVGElement::removedFromDocument(); 326 } 327 328 void SVGFontFaceElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) 329 { 330 SVGElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); 331 if (inDocument()) 332 rebuildFontFace(); 333 } 334 335 void SVGFontFaceElement::removeFromMappedElementSheet() 336 { 337 CSSStyleSheet* mappedElementSheet = document()->mappedElementSheet(); 338 if (!mappedElementSheet) 339 return; 340 341 for (unsigned i = 0; i < mappedElementSheet->length(); ++i) { 342 if (mappedElementSheet->item(i) == m_fontFaceRule) { 343 mappedElementSheet->remove(i); 344 break; 345 } 346 } 347 document()->updateStyleSelector(); 348 } 349 350 } // namespace WebCore 351 352 #endif // ENABLE(SVG_FONTS) 353