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