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