2 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
3 * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
4 * Copyright (C) 2008 Apple Inc. All rights reserved.
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.
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.
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.
25 #include "core/svg/SVGFontFaceElement.h"
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"
47 using namespace SVGNames;
49 inline SVGFontFaceElement::SVGFontFaceElement(Document& document)
50 : SVGElement(font_faceTag, document)
51 , m_fontFaceRule(StyleRuleFontFace::create())
52 , m_fontElement(nullptr)
55 RefPtrWillBeRawPtr<MutableStylePropertySet> styleDeclaration = MutableStylePropertySet::create(HTMLStandardMode);
56 m_fontFaceRule->setProperties(styleDeclaration.release());
59 DEFINE_NODE_FACTORY(SVGFontFaceElement)
61 static CSSPropertyID cssPropertyIdForFontFaceAttributeName(const QualifiedName& attrName)
63 if (!attrName.namespaceURI().isNull())
64 return CSSPropertyInvalid;
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);
106 return propertyNameToIdMap->get(attrName.localName().impl());
109 void SVGFontFaceElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
111 CSSPropertyID propId = cssPropertyIdForFontFaceAttributeName(name);
113 m_fontFaceRule->mutableProperties().setProperty(propId, value, false);
118 SVGElement::parseAttribute(name, value);
121 unsigned SVGFontFaceElement::unitsPerEm() const
123 const AtomicString& value = fastGetAttribute(units_per_emAttr);
125 return gDefaultUnitsPerEm;
127 return static_cast<unsigned>(ceilf(value.toFloat()));
130 int SVGFontFaceElement::xHeight() const
132 return static_cast<int>(ceilf(fastGetAttribute(x_heightAttr).toFloat()));
135 float SVGFontFaceElement::horizontalOriginX() const
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();
146 float SVGFontFaceElement::horizontalOriginY() const
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();
157 float SVGFontFaceElement::horizontalAdvanceX() const
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();
168 float SVGFontFaceElement::verticalOriginX() const
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);
178 return horizontalAdvanceX() / 2.0f;
180 return value.toFloat();
183 float SVGFontFaceElement::verticalOriginY() const
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);
195 return value.toFloat();
198 float SVGFontFaceElement::verticalAdvanceY() const
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);
209 return value.toFloat();
212 int SVGFontFaceElement::ascent() const
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()));
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()));
228 // Match Batiks default value
229 return static_cast<int>(ceilf(unitsPerEm() * 0.8f));
232 int SVGFontFaceElement::descent() const
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;
247 const AtomicString& vertOriginY = m_fontElement->fastGetAttribute(vert_origin_yAttr);
248 if (!vertOriginY.isEmpty())
249 return static_cast<int>(ceilf(vertOriginY.toFloat()));
252 // Match Batiks default value
253 return static_cast<int>(ceilf(unitsPerEm() * 0.2f));
256 String SVGFontFaceElement::fontFamily() const
258 return m_fontFaceRule->properties().getPropertyValue(CSSPropertyFontFamily);
261 SVGFontElement* SVGFontFaceElement::associatedFontElement() const
263 ASSERT(parentNode() == m_fontElement);
264 ASSERT(!parentNode() || isSVGFontElement(*parentNode()));
265 return m_fontElement;
268 void SVGFontFaceElement::rebuildFontFace()
271 ASSERT(!m_fontElement);
275 bool describesParentFont = isSVGFontElement(*parentNode());
276 RefPtrWillBeRawPtr<CSSValueList> list = nullptr;
278 if (describesParentFont) {
279 m_fontElement = toSVGFontElement(parentNode());
281 list = CSSValueList::createCommaSeparated();
282 list->append(CSSFontFaceSrcValue::createLocal(fontFamily()));
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();
290 if (!list || !list->length())
293 // Parse in-memory CSS rules
294 m_fontFaceRule->mutableProperties().addParsedProperty(CSSProperty(CSSPropertySrc, list));
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());
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);
308 document().styleResolverChanged();
311 Node::InsertionNotificationRequest SVGFontFaceElement::insertedInto(ContainerNode* rootParent)
313 SVGElement::insertedInto(rootParent);
314 if (!rootParent->inDocument()) {
315 ASSERT(!m_fontElement);
316 return InsertionDone;
318 document().accessSVGExtensions().registerSVGFontFaceElement(this);
321 return InsertionDone;
324 void SVGFontFaceElement::removedFrom(ContainerNode* rootParent)
326 SVGElement::removedFrom(rootParent);
328 if (rootParent->inDocument()) {
329 m_fontElement = nullptr;
330 document().accessSVGExtensions().unregisterSVGFontFaceElement(this);
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);
339 m_fontFaceRule->mutableProperties().clear();
340 document().styleResolverChanged();
342 ASSERT(!m_fontElement);
345 void SVGFontFaceElement::childrenChanged(const ChildrenChange& change)
347 SVGElement::childrenChanged(change);
351 void SVGFontFaceElement::trace(Visitor* visitor)
353 visitor->trace(m_fontFaceRule);
354 visitor->trace(m_fontElement);
355 SVGElement::trace(visitor);
360 #endif // ENABLE(SVG_FONTS)