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