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