• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "CString.h"
28 #include "CSSFontFaceRule.h"
29 #include "CSSFontFaceSrcValue.h"
30 #include "CSSParser.h"
31 #include "CSSProperty.h"
32 #include "CSSPropertyNames.h"
33 #include "CSSStyleSelector.h"
34 #include "CSSStyleSheet.h"
35 #include "CSSValueKeywords.h"
36 #include "CSSValueList.h"
37 #include "Document.h"
38 #include "Font.h"
39 #include "MappedAttribute.h"
40 #include "SVGFontElement.h"
41 #include "SVGFontFaceSrcElement.h"
42 #include "SVGGlyphElement.h"
43 #include "SVGNames.h"
44 #include <math.h>
45 
46 namespace WebCore {
47 
48 using namespace SVGNames;
49 
SVGFontFaceElement(const QualifiedName & tagName,Document * doc)50 SVGFontFaceElement::SVGFontFaceElement(const QualifiedName& tagName, Document* doc)
51     : SVGElement(tagName, doc)
52     , m_fontFaceRule(CSSFontFaceRule::create())
53     , m_styleDeclaration(CSSMutableStyleDeclaration::create())
54 {
55     m_styleDeclaration->setParent(document()->mappedElementSheet());
56     m_styleDeclaration->setStrictParsing(true);
57     m_fontFaceRule->setDeclaration(m_styleDeclaration.get());
58 }
59 
~SVGFontFaceElement()60 SVGFontFaceElement::~SVGFontFaceElement()
61 {
62     removeFromMappedElementSheet();
63 }
64 
cssPropertyIdForSVGAttributeName(const QualifiedName & attrName)65 static int cssPropertyIdForSVGAttributeName(const QualifiedName& attrName)
66 {
67     if (!attrName.namespaceURI().isNull())
68         return 0;
69 
70     static HashMap<AtomicStringImpl*, int>* propertyNameToIdMap = 0;
71     if (!propertyNameToIdMap) {
72         propertyNameToIdMap = new HashMap<AtomicStringImpl*, int>;
73         // This is a list of all @font-face CSS properties which are exposed as SVG XML attributes
74         // Those commented out are not yet supported by WebCore's style system
75         //mapAttributeToCSSProperty(propertyNameToIdMap, accent_heightAttr);
76         //mapAttributeToCSSProperty(propertyNameToIdMap, alphabeticAttr);
77         //mapAttributeToCSSProperty(propertyNameToIdMap, ascentAttr);
78         //mapAttributeToCSSProperty(propertyNameToIdMap, bboxAttr);
79         //mapAttributeToCSSProperty(propertyNameToIdMap, cap_heightAttr);
80         //mapAttributeToCSSProperty(propertyNameToIdMap, descentAttr);
81         mapAttributeToCSSProperty(propertyNameToIdMap, font_familyAttr);
82         mapAttributeToCSSProperty(propertyNameToIdMap, font_sizeAttr);
83         mapAttributeToCSSProperty(propertyNameToIdMap, font_stretchAttr);
84         mapAttributeToCSSProperty(propertyNameToIdMap, font_styleAttr);
85         mapAttributeToCSSProperty(propertyNameToIdMap, font_variantAttr);
86         mapAttributeToCSSProperty(propertyNameToIdMap, font_weightAttr);
87         //mapAttributeToCSSProperty(propertyNameToIdMap, hangingAttr);
88         //mapAttributeToCSSProperty(propertyNameToIdMap, ideographicAttr);
89         //mapAttributeToCSSProperty(propertyNameToIdMap, mathematicalAttr);
90         //mapAttributeToCSSProperty(propertyNameToIdMap, overline_positionAttr);
91         //mapAttributeToCSSProperty(propertyNameToIdMap, overline_thicknessAttr);
92         //mapAttributeToCSSProperty(propertyNameToIdMap, panose_1Attr);
93         //mapAttributeToCSSProperty(propertyNameToIdMap, slopeAttr);
94         //mapAttributeToCSSProperty(propertyNameToIdMap, stemhAttr);
95         //mapAttributeToCSSProperty(propertyNameToIdMap, stemvAttr);
96         //mapAttributeToCSSProperty(propertyNameToIdMap, strikethrough_positionAttr);
97         //mapAttributeToCSSProperty(propertyNameToIdMap, strikethrough_thicknessAttr);
98         //mapAttributeToCSSProperty(propertyNameToIdMap, underline_positionAttr);
99         //mapAttributeToCSSProperty(propertyNameToIdMap, underline_thicknessAttr);
100         //mapAttributeToCSSProperty(propertyNameToIdMap, unicode_rangeAttr);
101         //mapAttributeToCSSProperty(propertyNameToIdMap, units_per_emAttr);
102         //mapAttributeToCSSProperty(propertyNameToIdMap, v_alphabeticAttr);
103         //mapAttributeToCSSProperty(propertyNameToIdMap, v_hangingAttr);
104         //mapAttributeToCSSProperty(propertyNameToIdMap, v_ideographicAttr);
105         //mapAttributeToCSSProperty(propertyNameToIdMap, v_mathematicalAttr);
106         //mapAttributeToCSSProperty(propertyNameToIdMap, widthsAttr);
107         //mapAttributeToCSSProperty(propertyNameToIdMap, x_heightAttr);
108     }
109 
110     return propertyNameToIdMap->get(attrName.localName().impl());
111 }
112 
parseMappedAttribute(MappedAttribute * attr)113 void SVGFontFaceElement::parseMappedAttribute(MappedAttribute* attr)
114 {
115     int propId = cssPropertyIdForSVGAttributeName(attr->name());
116     if (propId > 0) {
117         m_styleDeclaration->setProperty(propId, attr->value(), false);
118         if (inDocument())
119             rebuildFontFace();
120         return;
121     }
122 
123     SVGElement::parseMappedAttribute(attr);
124 }
125 
unitsPerEm() const126 unsigned SVGFontFaceElement::unitsPerEm() const
127 {
128     const AtomicString& value = getAttribute(units_per_emAttr);
129     if (value.isEmpty())
130         return defaultUnitsPerEm;
131 
132     return static_cast<unsigned>(ceilf(value.toFloat()));
133 }
134 
xHeight() const135 int SVGFontFaceElement::xHeight() const
136 {
137     return static_cast<int>(ceilf(getAttribute(x_heightAttr).toFloat()));
138 }
139 
horizontalOriginX() const140 float SVGFontFaceElement::horizontalOriginX() const
141 {
142     if (!m_fontElement)
143         return 0.0f;
144 
145     // Spec: The X-coordinate in the font coordinate system of the origin of a glyph to be used when
146     // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.)
147     // If the attribute is not specified, the effect is as if a value of "0" were specified.
148     return m_fontElement->getAttribute(horiz_origin_xAttr).toFloat();
149 }
150 
horizontalOriginY() const151 float SVGFontFaceElement::horizontalOriginY() const
152 {
153     if (!m_fontElement)
154         return 0.0f;
155 
156     // Spec: The Y-coordinate in the font coordinate system of the origin of a glyph to be used when
157     // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.)
158     // If the attribute is not specified, the effect is as if a value of "0" were specified.
159     return m_fontElement->getAttribute(horiz_origin_yAttr).toFloat();
160 }
161 
horizontalAdvanceX() const162 float SVGFontFaceElement::horizontalAdvanceX() const
163 {
164     if (!m_fontElement)
165         return 0.0f;
166 
167     // Spec: The default horizontal advance after rendering a glyph in horizontal orientation. Glyph
168     // widths are required to be non-negative, even if the glyph is typically rendered right-to-left,
169     // as in Hebrew and Arabic scripts.
170     return m_fontElement->getAttribute(horiz_adv_xAttr).toFloat();
171 }
172 
verticalOriginX() const173 float SVGFontFaceElement::verticalOriginX() const
174 {
175     if (!m_fontElement)
176         return 0.0f;
177 
178     // Spec: The default X-coordinate in the font coordinate system of the origin of a glyph to be used when
179     // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute
180     // were set to half of the effective value of attribute horiz-adv-x.
181     const AtomicString& value = m_fontElement->getAttribute(vert_origin_xAttr);
182     if (value.isEmpty())
183         return horizontalAdvanceX() / 2.0f;
184 
185     return value.toFloat();
186 }
187 
verticalOriginY() const188 float SVGFontFaceElement::verticalOriginY() const
189 {
190     if (!m_fontElement)
191         return 0.0f;
192 
193     // Spec: The default Y-coordinate in the font coordinate system of the origin of a glyph to be used when
194     // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute
195     // were set to the position specified by the font's ascent attribute.
196     const AtomicString& value = m_fontElement->getAttribute(vert_origin_yAttr);
197     if (value.isEmpty())
198         return ascent();
199 
200     return value.toFloat();
201 }
202 
verticalAdvanceY() const203 float SVGFontFaceElement::verticalAdvanceY() const
204 {
205     if (!m_fontElement)
206         return 0.0f;
207 
208     // Spec: The default vertical advance after rendering a glyph in vertical orientation. If the attribute is
209     // not specified, the effect is as if a value equivalent of one em were specified (see units-per-em).
210     const AtomicString& value = m_fontElement->getAttribute(vert_adv_yAttr);
211        if (value.isEmpty())
212         return 1.0f;
213 
214     return value.toFloat();
215 }
216 
ascent() const217 int SVGFontFaceElement::ascent() const
218 {
219     // Spec: Same syntax and semantics as the 'ascent' descriptor within an @font-face rule. The maximum
220     // unaccented height of the font within the font coordinate system. If the attribute is not specified,
221     // the effect is as if the attribute were set to the difference between the units-per-em value and the
222     // vert-origin-y value for the corresponding font.
223     const AtomicString& ascentValue = getAttribute(ascentAttr);
224     if (!ascentValue.isEmpty())
225         return static_cast<int>(ceilf(ascentValue.toFloat()));
226 
227     if (m_fontElement) {
228         const AtomicString& vertOriginY = m_fontElement->getAttribute(vert_origin_yAttr);
229         if (!vertOriginY.isEmpty())
230             return static_cast<int>(unitsPerEm()) - static_cast<int>(ceilf(vertOriginY.toFloat()));
231     }
232 
233     // Match Batiks default value
234     return static_cast<int>(ceilf(unitsPerEm() * 0.8f));
235 }
236 
descent() const237 int SVGFontFaceElement::descent() const
238 {
239     // Spec: Same syntax and semantics as the 'descent' descriptor within an @font-face rule. The maximum
240     // unaccented depth of the font within the font coordinate system. If the attribute is not specified,
241     // the effect is as if the attribute were set to the vert-origin-y value for the corresponding font.
242     const AtomicString& descentValue = getAttribute(descentAttr);
243     if (!descentValue.isEmpty()) {
244         // 14 different W3C SVG 1.1 testcases use a negative descent value,
245         // where a positive was meant to be used  Including:
246         // animate-elem-24-t.svg, fonts-elem-01-t.svg, fonts-elem-02-t.svg (and 11 others)
247         int descent = static_cast<int>(ceilf(descentValue.toFloat()));
248         return descent < 0 ? -descent : descent;
249     }
250 
251     if (m_fontElement) {
252         const AtomicString& vertOriginY = m_fontElement->getAttribute(vert_origin_yAttr);
253         if (!vertOriginY.isEmpty())
254             return static_cast<int>(ceilf(vertOriginY.toFloat()));
255     }
256 
257     // Match Batiks default value
258     return static_cast<int>(ceilf(unitsPerEm() * 0.2f));
259 }
260 
fontFamily() const261 String SVGFontFaceElement::fontFamily() const
262 {
263     return m_styleDeclaration->getPropertyValue(CSSPropertyFontFamily);
264 }
265 
rebuildFontFace()266 void SVGFontFaceElement::rebuildFontFace()
267 {
268     ASSERT(inDocument());
269 
270     // we currently ignore all but the first src element, alternatively we could concat them
271     SVGFontFaceSrcElement* srcElement = 0;
272 
273     for (Node* child = firstChild(); child && !srcElement; child = child->nextSibling()) {
274         if (child->hasTagName(font_face_srcTag))
275             srcElement = static_cast<SVGFontFaceSrcElement*>(child);
276     }
277 
278     bool describesParentFont = parentNode()->hasTagName(SVGNames::fontTag);
279     RefPtr<CSSValueList> list;
280 
281     if (describesParentFont) {
282         m_fontElement = static_cast<SVGFontElement*>(parentNode());
283 
284         list = CSSValueList::createCommaSeparated();
285         list->append(CSSFontFaceSrcValue::createLocal(fontFamily()));
286     } else {
287         m_fontElement = 0;
288         if (srcElement)
289             list = srcElement->srcValue();
290     }
291 
292     if (!list)
293         return;
294 
295     // Parse in-memory CSS rules
296     CSSProperty srcProperty(CSSPropertySrc, list);
297     const CSSProperty* srcPropertyRef = &srcProperty;
298     m_styleDeclaration->addParsedProperties(&srcPropertyRef, 1);
299 
300     if (describesParentFont) {
301         // Traverse parsed CSS values and associate CSSFontFaceSrcValue elements with ourselves.
302         RefPtr<CSSValue> src = m_styleDeclaration->getPropertyCSSValue(CSSPropertySrc);
303         CSSValueList* srcList = static_cast<CSSValueList*>(src.get());
304 
305         unsigned srcLength = srcList ? srcList->length() : 0;
306         for (unsigned i = 0; i < srcLength; i++) {
307             if (CSSFontFaceSrcValue* item = static_cast<CSSFontFaceSrcValue*>(srcList->itemWithoutBoundsCheck(i)))
308                 item->setSVGFontFaceElement(this);
309         }
310     }
311 
312     document()->updateStyleSelector();
313 }
314 
insertedIntoDocument()315 void SVGFontFaceElement::insertedIntoDocument()
316 {
317     SVGElement::insertedIntoDocument();
318     document()->mappedElementSheet()->append(m_fontFaceRule);
319     rebuildFontFace();
320 }
321 
removedFromDocument()322 void SVGFontFaceElement::removedFromDocument()
323 {
324     removeFromMappedElementSheet();
325     SVGElement::removedFromDocument();
326 }
327 
childrenChanged(bool changedByParser,Node * beforeChange,Node * afterChange,int childCountDelta)328 void SVGFontFaceElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
329 {
330     SVGElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
331     if (inDocument())
332         rebuildFontFace();
333 }
334 
removeFromMappedElementSheet()335 void SVGFontFaceElement::removeFromMappedElementSheet()
336 {
337     CSSStyleSheet* mappedElementSheet = document()->mappedElementSheet();
338     if (!mappedElementSheet)
339         return;
340 
341     for (unsigned i = 0; i < mappedElementSheet->length(); ++i) {
342         if (mappedElementSheet->item(i) == m_fontFaceRule) {
343             mappedElementSheet->remove(i);
344             break;
345         }
346     }
347     document()->updateStyleSelector();
348 }
349 
350 } // namespace WebCore
351 
352 #endif // ENABLE(SVG_FONTS)
353