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