1/*
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.
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
45namespace blink {
46
47using namespace SVGNames;
48
49inline 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
59DEFINE_NODE_FACTORY(SVGFontFaceElement)
60
61static 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
109void 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
121unsigned 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
130int SVGFontFaceElement::xHeight() const
131{
132    return static_cast<int>(ceilf(fastGetAttribute(x_heightAttr).toFloat()));
133}
134
135float 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
146float 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
157float 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
168float 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
183float 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
198float 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
212int 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
232int 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
256String SVGFontFaceElement::fontFamily() const
257{
258    return m_fontFaceRule->properties().getPropertyValue(CSSPropertyFontFamily);
259}
260
261SVGFontElement* SVGFontFaceElement::associatedFontElement() const
262{
263    ASSERT(parentNode() == m_fontElement);
264    ASSERT(!parentNode() || isSVGFontElement(*parentNode()));
265    return m_fontElement;
266}
267
268void 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
311Node::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
324void 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
345void SVGFontFaceElement::childrenChanged(const ChildrenChange& change)
346{
347    SVGElement::childrenChanged(change);
348    rebuildFontFace();
349}
350
351void 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