Optimize strings copies in srcset parser
[WebKit-https.git] / Source / WebCore / svg / SVGFontFaceElement.cpp
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 "SVGFontFaceElement.h"
26
27 #include "Attribute.h"
28 #include "CSSFontFaceSrcValue.h"
29 #include "CSSParser.h"
30 #include "CSSPropertyNames.h"
31 #include "CSSStyleSheet.h"
32 #include "CSSValueKeywords.h"
33 #include "CSSValueList.h"
34 #include "Document.h"
35 #include "ElementIterator.h"
36 #include "Font.h"
37 #include "SVGDocumentExtensions.h"
38 #include "SVGFontElement.h"
39 #include "SVGFontFaceSrcElement.h"
40 #include "SVGGlyphElement.h"
41 #include "SVGNames.h"
42 #include "StylePropertySet.h"
43 #include "StyleResolver.h"
44 #include "StyleRule.h"
45 #include <math.h>
46
47 namespace WebCore {
48
49 using namespace SVGNames;
50
51 inline SVGFontFaceElement::SVGFontFaceElement(const QualifiedName& tagName, Document& document)
52     : SVGElement(tagName, document)
53     , m_fontFaceRule(StyleRuleFontFace::create(MutableStylePropertySet::create(CSSStrictMode)))
54     , m_fontElement(0)
55 {
56     ASSERT(hasTagName(font_faceTag));
57 }
58
59 PassRefPtr<SVGFontFaceElement> SVGFontFaceElement::create(const QualifiedName& tagName, Document& document)
60 {
61     return adoptRef(new SVGFontFaceElement(tagName, document));
62 }
63
64 void SVGFontFaceElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
65 {    
66     CSSPropertyID propId = cssPropertyIdForSVGAttributeName(name);
67     if (propId > 0) {
68         m_fontFaceRule->mutableProperties()->setProperty(propId, value, false);
69         rebuildFontFace();
70         return;
71     }
72     
73     SVGElement::parseAttribute(name, value);
74 }
75
76 unsigned SVGFontFaceElement::unitsPerEm() const
77 {
78     const AtomicString& value = fastGetAttribute(units_per_emAttr);
79     if (value.isEmpty())
80         return gDefaultUnitsPerEm;
81
82     return static_cast<unsigned>(ceilf(value.toFloat()));
83 }
84
85 int SVGFontFaceElement::xHeight() const
86 {
87     return static_cast<int>(ceilf(fastGetAttribute(x_heightAttr).toFloat()));
88 }
89
90 float SVGFontFaceElement::horizontalOriginX() const
91 {
92     if (!m_fontElement)
93         return 0.0f;
94
95     // Spec: The X-coordinate in the font coordinate system of the origin of a glyph to be used when
96     // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.)
97     // If the attribute is not specified, the effect is as if a value of "0" were specified.
98     return m_fontElement->fastGetAttribute(horiz_origin_xAttr).toFloat();
99 }
100
101 float SVGFontFaceElement::horizontalOriginY() const
102 {
103     if (!m_fontElement)
104         return 0.0f;
105
106     // Spec: The Y-coordinate in the font coordinate system of the origin of a glyph to be used when
107     // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.)
108     // If the attribute is not specified, the effect is as if a value of "0" were specified.
109     return m_fontElement->fastGetAttribute(horiz_origin_yAttr).toFloat();
110 }
111
112 float SVGFontFaceElement::horizontalAdvanceX() const
113 {
114     if (!m_fontElement)
115         return 0.0f;
116
117     // Spec: The default horizontal advance after rendering a glyph in horizontal orientation. Glyph
118     // widths are required to be non-negative, even if the glyph is typically rendered right-to-left,
119     // as in Hebrew and Arabic scripts.
120     return m_fontElement->fastGetAttribute(horiz_adv_xAttr).toFloat();
121 }
122
123 float SVGFontFaceElement::verticalOriginX() const
124 {
125     if (!m_fontElement)
126         return 0.0f;
127
128     // Spec: The default X-coordinate in the font coordinate system of the origin of a glyph to be used when
129     // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute
130     // were set to half of the effective value of attribute horiz-adv-x.
131     const AtomicString& value = m_fontElement->fastGetAttribute(vert_origin_xAttr);
132     if (value.isEmpty())
133         return horizontalAdvanceX() / 2.0f;
134
135     return value.toFloat();
136 }
137
138 float SVGFontFaceElement::verticalOriginY() const
139 {
140     if (!m_fontElement)
141         return 0.0f;
142
143     // Spec: The default Y-coordinate in the font coordinate system of the origin of a glyph to be used when
144     // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute
145     // were set to the position specified by the font's ascent attribute.             
146     const AtomicString& value = m_fontElement->fastGetAttribute(vert_origin_yAttr);
147     if (value.isEmpty())
148         return ascent();
149
150     return value.toFloat();
151 }
152
153 float SVGFontFaceElement::verticalAdvanceY() const
154 {
155     if (!m_fontElement)
156         return 0.0f;
157
158     // Spec: The default vertical advance after rendering a glyph in vertical orientation. If the attribute is
159     // not specified, the effect is as if a value equivalent of one em were specified (see units-per-em).                    
160     const AtomicString& value = m_fontElement->fastGetAttribute(vert_adv_yAttr);
161        if (value.isEmpty())
162         return 1.0f;
163
164     return value.toFloat();
165 }
166
167 int SVGFontFaceElement::ascent() const
168 {
169     // Spec: Same syntax and semantics as the 'ascent' descriptor within an @font-face rule. The maximum
170     // unaccented height of the font within the font coordinate system. If the attribute is not specified,
171     // the effect is as if the attribute were set to the difference between the units-per-em value and the
172     // vert-origin-y value for the corresponding font.
173     const AtomicString& ascentValue = fastGetAttribute(ascentAttr);
174     if (!ascentValue.isEmpty())
175         return static_cast<int>(ceilf(ascentValue.toFloat()));
176
177     if (m_fontElement) {
178         const AtomicString& vertOriginY = m_fontElement->fastGetAttribute(vert_origin_yAttr);
179         if (!vertOriginY.isEmpty())
180             return static_cast<int>(unitsPerEm()) - static_cast<int>(ceilf(vertOriginY.toFloat()));
181     }
182
183     // Match Batiks default value
184     return static_cast<int>(ceilf(unitsPerEm() * 0.8f));
185 }
186
187 int SVGFontFaceElement::descent() const
188 {
189     // Spec: Same syntax and semantics as the 'descent' descriptor within an @font-face rule. The maximum
190     // unaccented depth of the font within the font coordinate system. If the attribute is not specified,
191     // the effect is as if the attribute were set to the vert-origin-y value for the corresponding font.
192     const AtomicString& descentValue = fastGetAttribute(descentAttr);
193     if (!descentValue.isEmpty()) {
194         // 14 different W3C SVG 1.1 testcases use a negative descent value,
195         // where a positive was meant to be used  Including:
196         // animate-elem-24-t.svg, fonts-elem-01-t.svg, fonts-elem-02-t.svg (and 11 others)
197         int descent = static_cast<int>(ceilf(descentValue.toFloat()));
198         return descent < 0 ? -descent : descent;
199     }
200
201     if (m_fontElement) {
202         const AtomicString& vertOriginY = m_fontElement->fastGetAttribute(vert_origin_yAttr);
203         if (!vertOriginY.isEmpty())
204             return static_cast<int>(ceilf(vertOriginY.toFloat()));
205     }
206
207     // Match Batiks default value
208     return static_cast<int>(ceilf(unitsPerEm() * 0.2f));
209 }
210
211 String SVGFontFaceElement::fontFamily() const
212 {
213     return m_fontFaceRule->properties().getPropertyValue(CSSPropertyFontFamily);
214 }
215
216 SVGFontElement* SVGFontFaceElement::associatedFontElement() const
217 {
218     ASSERT(parentNode() == m_fontElement);
219     ASSERT(!parentNode() || isSVGFontElement(parentNode()));
220     return m_fontElement;
221 }
222
223 void SVGFontFaceElement::rebuildFontFace()
224 {
225     if (!inDocument()) {
226         ASSERT(!m_fontElement);
227         return;
228     }
229
230     // we currently ignore all but the first src element, alternatively we could concat them
231     auto srcElement = childrenOfType<SVGFontFaceSrcElement>(this).first();
232
233     bool describesParentFont = isSVGFontElement(parentNode());
234     RefPtr<CSSValueList> list;
235
236     if (describesParentFont) {
237         m_fontElement = toSVGFontElement(parentNode());
238
239         list = CSSValueList::createCommaSeparated();
240         list->append(CSSFontFaceSrcValue::createLocal(fontFamily()));
241     } else {
242         m_fontElement = 0;
243         if (srcElement)
244             list = srcElement->srcValue();
245     }
246
247     if (!list || !list->length())
248         return;
249
250     // Parse in-memory CSS rules
251     m_fontFaceRule->mutableProperties()->addParsedProperty(CSSProperty(CSSPropertySrc, list));
252
253     if (describesParentFont) {    
254         // Traverse parsed CSS values and associate CSSFontFaceSrcValue elements with ourselves.
255         RefPtr<CSSValue> src = m_fontFaceRule->properties().getPropertyCSSValue(CSSPropertySrc);
256         CSSValueList* srcList = toCSSValueList(src.get());
257
258         unsigned srcLength = srcList ? srcList->length() : 0;
259         for (unsigned i = 0; i < srcLength; i++) {
260             if (CSSFontFaceSrcValue* item = toCSSFontFaceSrcValue(srcList->itemWithoutBoundsCheck(i)))
261                 item->setSVGFontFaceElement(this);
262         }
263     }
264
265     document().styleResolverChanged(DeferRecalcStyle);
266 }
267
268 Node::InsertionNotificationRequest SVGFontFaceElement::insertedInto(ContainerNode* rootParent)
269 {
270     SVGElement::insertedInto(rootParent);
271     if (!rootParent->inDocument()) {
272         ASSERT(!m_fontElement);
273         return InsertionDone;
274     }
275     document().accessSVGExtensions()->registerSVGFontFaceElement(this);
276
277     rebuildFontFace();
278     return InsertionDone;
279 }
280
281 void SVGFontFaceElement::removedFrom(ContainerNode* rootParent)
282 {
283     SVGElement::removedFrom(rootParent);
284
285     if (rootParent->inDocument()) {
286         m_fontElement = 0;
287         document().accessSVGExtensions()->unregisterSVGFontFaceElement(this);
288         m_fontFaceRule->mutableProperties()->clear();
289
290         document().styleResolverChanged(DeferRecalcStyle);
291     } else
292         ASSERT(!m_fontElement);
293 }
294
295 void SVGFontFaceElement::childrenChanged(const ChildChange& change)
296 {
297     SVGElement::childrenChanged(change);
298     rebuildFontFace();
299 }
300
301 } // namespace WebCore
302
303 #endif // ENABLE(SVG_FONTS)