OLD | NEW |
| (Empty) |
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/css/parser/CSSParser.h" | |
36 #include "core/dom/Attribute.h" | |
37 #include "core/dom/Document.h" | |
38 #include "core/dom/StyleEngine.h" | |
39 #include "core/svg/SVGDocumentExtensions.h" | |
40 #include "core/svg/SVGFontElement.h" | |
41 #include "core/svg/SVGFontFaceSrcElement.h" | |
42 #include "core/svg/SVGGlyphElement.h" | |
43 #include "platform/fonts/Font.h" | |
44 #include <math.h> | |
45 | |
46 namespace blink { | |
47 | |
48 using namespace SVGNames; | |
49 | |
50 inline SVGFontFaceElement::SVGFontFaceElement(Document& document) | |
51 : SVGElement(font_faceTag, document) | |
52 , m_fontFaceRule(StyleRuleFontFace::create()) | |
53 , m_fontElement(nullptr) | |
54 , m_weakFactory(this) | |
55 { | |
56 RefPtrWillBeRawPtr<MutableStylePropertySet> styleDeclaration = MutableStyleP
ropertySet::create(HTMLStandardMode); | |
57 m_fontFaceRule->setProperties(styleDeclaration.release()); | |
58 } | |
59 | |
60 DEFINE_NODE_FACTORY(SVGFontFaceElement) | |
61 | |
62 static CSSPropertyID cssPropertyIdForFontFaceAttributeName(const QualifiedName&
attrName) | |
63 { | |
64 if (!attrName.namespaceURI().isNull()) | |
65 return CSSPropertyInvalid; | |
66 | |
67 static HashMap<StringImpl*, CSSPropertyID>* propertyNameToIdMap = 0; | |
68 if (!propertyNameToIdMap) { | |
69 propertyNameToIdMap = new HashMap<StringImpl*, CSSPropertyID>; | |
70 // This is a list of all @font-face CSS properties which are exposed as
SVG XML attributes | |
71 // Those commented out are not yet supported by WebCore's style system | |
72 const QualifiedName* const attrNames[] = { | |
73 // &accent_heightAttr, | |
74 // &alphabeticAttr, | |
75 // &ascentAttr, | |
76 // &bboxAttr, | |
77 // &cap_heightAttr, | |
78 // &descentAttr, | |
79 &font_familyAttr, | |
80 &font_sizeAttr, | |
81 &font_stretchAttr, | |
82 &font_styleAttr, | |
83 &font_variantAttr, | |
84 &font_weightAttr, | |
85 // &hangingAttr, | |
86 // &ideographicAttr, | |
87 // &mathematicalAttr, | |
88 // &overline_positionAttr, | |
89 // &overline_thicknessAttr, | |
90 // &panose_1Attr, | |
91 // &slopeAttr, | |
92 // &stemhAttr, | |
93 // &stemvAttr, | |
94 // &strikethrough_positionAttr, | |
95 // &strikethrough_thicknessAttr, | |
96 // &underline_positionAttr, | |
97 // &underline_thicknessAttr, | |
98 // &unicode_rangeAttr, | |
99 // &units_per_emAttr, | |
100 // &v_alphabeticAttr, | |
101 // &v_hangingAttr, | |
102 // &v_ideographicAttr, | |
103 // &v_mathematicalAttr, | |
104 // &widthsAttr, | |
105 // &x_heightAttr, | |
106 }; | |
107 for (size_t i = 0; i < WTF_ARRAY_LENGTH(attrNames); i++) { | |
108 CSSPropertyID propertyId = cssPropertyID(attrNames[i]->localName()); | |
109 ASSERT(propertyId > 0); | |
110 propertyNameToIdMap->set(attrNames[i]->localName().impl(), propertyI
d); | |
111 } | |
112 } | |
113 | |
114 return propertyNameToIdMap->get(attrName.localName().impl()); | |
115 } | |
116 | |
117 void SVGFontFaceElement::parseAttribute(const QualifiedName& name, const AtomicS
tring& value) | |
118 { | |
119 CSSPropertyID propId = cssPropertyIdForFontFaceAttributeName(name); | |
120 if (propId > 0) { | |
121 m_fontFaceRule->mutableProperties().setProperty(propId, value, false); | |
122 rebuildFontFace(); | |
123 return; | |
124 } | |
125 | |
126 SVGElement::parseAttribute(name, value); | |
127 } | |
128 | |
129 unsigned SVGFontFaceElement::unitsPerEm() const | |
130 { | |
131 const AtomicString& value = fastGetAttribute(units_per_emAttr); | |
132 if (value.isEmpty()) | |
133 return gDefaultUnitsPerEm; | |
134 | |
135 return static_cast<unsigned>(ceilf(value.toFloat())); | |
136 } | |
137 | |
138 int SVGFontFaceElement::xHeight() const | |
139 { | |
140 return static_cast<int>(ceilf(fastGetAttribute(x_heightAttr).toFloat())); | |
141 } | |
142 | |
143 float SVGFontFaceElement::horizontalOriginX() const | |
144 { | |
145 if (!m_fontElement) | |
146 return 0.0f; | |
147 | |
148 // Spec: The X-coordinate in the font coordinate system of the origin of a g
lyph to be used when | |
149 // drawing horizontally oriented text. (Note that the origin applies to all
glyphs in the font.) | |
150 // If the attribute is not specified, the effect is as if a value of "0" wer
e specified. | |
151 return m_fontElement->fastGetAttribute(horiz_origin_xAttr).toFloat(); | |
152 } | |
153 | |
154 float SVGFontFaceElement::horizontalOriginY() const | |
155 { | |
156 if (!m_fontElement) | |
157 return 0.0f; | |
158 | |
159 // Spec: The Y-coordinate in the font coordinate system of the origin of a g
lyph to be used when | |
160 // drawing horizontally oriented text. (Note that the origin applies to all
glyphs in the font.) | |
161 // If the attribute is not specified, the effect is as if a value of "0" wer
e specified. | |
162 return m_fontElement->fastGetAttribute(horiz_origin_yAttr).toFloat(); | |
163 } | |
164 | |
165 float SVGFontFaceElement::horizontalAdvanceX() const | |
166 { | |
167 if (!m_fontElement) | |
168 return 0.0f; | |
169 | |
170 // Spec: The default horizontal advance after rendering a glyph in horizonta
l orientation. Glyph | |
171 // widths are required to be non-negative, even if the glyph is typically re
ndered right-to-left, | |
172 // as in Hebrew and Arabic scripts. | |
173 return m_fontElement->fastGetAttribute(horiz_adv_xAttr).toFloat(); | |
174 } | |
175 | |
176 float SVGFontFaceElement::verticalOriginX() const | |
177 { | |
178 if (!m_fontElement) | |
179 return 0.0f; | |
180 | |
181 // Spec: The default X-coordinate in the font coordinate system of the origi
n of a glyph to be used when | |
182 // drawing vertically oriented text. If the attribute is not specified, the
effect is as if the attribute | |
183 // were set to half of the effective value of attribute horiz-adv-x. | |
184 const AtomicString& value = m_fontElement->fastGetAttribute(vert_origin_xAtt
r); | |
185 if (value.isEmpty()) | |
186 return horizontalAdvanceX() / 2.0f; | |
187 | |
188 return value.toFloat(); | |
189 } | |
190 | |
191 float SVGFontFaceElement::verticalOriginY() const | |
192 { | |
193 if (!m_fontElement) | |
194 return 0.0f; | |
195 | |
196 // Spec: The default Y-coordinate in the font coordinate system of the origi
n of a glyph to be used when | |
197 // drawing vertically oriented text. If the attribute is not specified, the
effect is as if the attribute | |
198 // were set to the position specified by the font's ascent attribute. | |
199 const AtomicString& value = m_fontElement->fastGetAttribute(vert_origin_yAtt
r); | |
200 if (value.isEmpty()) | |
201 return ascent(); | |
202 | |
203 return value.toFloat(); | |
204 } | |
205 | |
206 float SVGFontFaceElement::verticalAdvanceY() const | |
207 { | |
208 if (!m_fontElement) | |
209 return 0.0f; | |
210 | |
211 // Spec: The default vertical advance after rendering a glyph in vertical or
ientation. If the attribute is | |
212 // not specified, the effect is as if a value equivalent of one em were spec
ified (see units-per-em). | |
213 const AtomicString& value = m_fontElement->fastGetAttribute(vert_adv_yAttr); | |
214 if (value.isEmpty()) | |
215 return 1.0f; | |
216 | |
217 return value.toFloat(); | |
218 } | |
219 | |
220 int SVGFontFaceElement::ascent() const | |
221 { | |
222 // Spec: Same syntax and semantics as the 'ascent' descriptor within an @fon
t-face rule. The maximum | |
223 // unaccented height of the font within the font coordinate system. If the a
ttribute is not specified, | |
224 // the effect is as if the attribute were set to the difference between the
units-per-em value and the | |
225 // vert-origin-y value for the corresponding font. | |
226 const AtomicString& ascentValue = fastGetAttribute(ascentAttr); | |
227 if (!ascentValue.isEmpty()) | |
228 return static_cast<int>(ceilf(ascentValue.toFloat())); | |
229 | |
230 if (m_fontElement) { | |
231 const AtomicString& vertOriginY = m_fontElement->fastGetAttribute(vert_o
rigin_yAttr); | |
232 if (!vertOriginY.isEmpty()) | |
233 return static_cast<int>(unitsPerEm()) - static_cast<int>(ceilf(vertO
riginY.toFloat())); | |
234 } | |
235 | |
236 // Match Batiks default value | |
237 return static_cast<int>(ceilf(unitsPerEm() * 0.8f)); | |
238 } | |
239 | |
240 int SVGFontFaceElement::descent() const | |
241 { | |
242 // Spec: Same syntax and semantics as the 'descent' descriptor within an @fo
nt-face rule. The maximum | |
243 // unaccented depth of the font within the font coordinate system. If the at
tribute is not specified, | |
244 // the effect is as if the attribute were set to the vert-origin-y value for
the corresponding font. | |
245 const AtomicString& descentValue = fastGetAttribute(descentAttr); | |
246 if (!descentValue.isEmpty()) { | |
247 // 14 different W3C SVG 1.1 testcases use a negative descent value, | |
248 // where a positive was meant to be used Including: | |
249 // animate-elem-24-t.svg, fonts-elem-01-t.svg, fonts-elem-02-t.svg (and
11 others) | |
250 int descent = static_cast<int>(ceilf(descentValue.toFloat())); | |
251 return descent < 0 ? -descent : descent; | |
252 } | |
253 | |
254 if (m_fontElement) { | |
255 const AtomicString& vertOriginY = m_fontElement->fastGetAttribute(vert_o
rigin_yAttr); | |
256 if (!vertOriginY.isEmpty()) | |
257 return static_cast<int>(ceilf(vertOriginY.toFloat())); | |
258 } | |
259 | |
260 // Match Batiks default value | |
261 return static_cast<int>(ceilf(unitsPerEm() * 0.2f)); | |
262 } | |
263 | |
264 String SVGFontFaceElement::fontFamily() const | |
265 { | |
266 return m_fontFaceRule->properties().getPropertyValue(CSSPropertyFontFamily); | |
267 } | |
268 | |
269 SVGFontElement* SVGFontFaceElement::associatedFontElement() const | |
270 { | |
271 ASSERT(parentNode() == m_fontElement); | |
272 ASSERT(!parentNode() || isSVGFontElement(*parentNode())); | |
273 return m_fontElement; | |
274 } | |
275 | |
276 void SVGFontFaceElement::rebuildFontFace() | |
277 { | |
278 if (!inDocument()) { | |
279 ASSERT(!m_fontElement); | |
280 return; | |
281 } | |
282 | |
283 bool describesParentFont = isSVGFontElement(*parentNode()); | |
284 RefPtrWillBeRawPtr<CSSValueList> list = nullptr; | |
285 | |
286 if (describesParentFont) { | |
287 m_fontElement = toSVGFontElement(parentNode()); | |
288 | |
289 list = CSSValueList::createCommaSeparated(); | |
290 list->append(CSSFontFaceSrcValue::createLocal(fontFamily())); | |
291 } else { | |
292 m_fontElement = nullptr; | |
293 // we currently ignore all but the last src element, alternatively we co
uld concat them | |
294 if (SVGFontFaceSrcElement* element = Traversal<SVGFontFaceSrcElement>::l
astChild(*this)) | |
295 list = element->srcValue(); | |
296 } | |
297 | |
298 if (!list || !list->length()) | |
299 return; | |
300 | |
301 // Parse in-memory CSS rules | |
302 m_fontFaceRule->mutableProperties().addParsedProperty(CSSProperty(CSSPropert
ySrc, list)); | |
303 | |
304 if (describesParentFont) { | |
305 // Traverse parsed CSS values and associate CSSFontFaceSrcValue elements
with ourselves. | |
306 RefPtrWillBeRawPtr<CSSValue> src = m_fontFaceRule->properties().getPrope
rtyCSSValue(CSSPropertySrc); | |
307 CSSValueList* srcList = toCSSValueList(src.get()); | |
308 | |
309 unsigned srcLength = srcList ? srcList->length() : 0; | |
310 for (unsigned i = 0; i < srcLength; i++) { | |
311 if (CSSFontFaceSrcValue* item = toCSSFontFaceSrcValue(srcList->item(
i))) | |
312 item->setSVGFontFaceElement(this); | |
313 } | |
314 } | |
315 | |
316 document().styleResolverChanged(); | |
317 } | |
318 | |
319 Node::InsertionNotificationRequest SVGFontFaceElement::insertedInto(ContainerNod
e* rootParent) | |
320 { | |
321 SVGElement::insertedInto(rootParent); | |
322 if (!rootParent->inDocument()) { | |
323 ASSERT(!m_fontElement); | |
324 return InsertionDone; | |
325 } | |
326 document().accessSVGExtensions().registerSVGFontFaceElement(this); | |
327 | |
328 rebuildFontFace(); | |
329 return InsertionDone; | |
330 } | |
331 | |
332 void SVGFontFaceElement::removedFrom(ContainerNode* rootParent) | |
333 { | |
334 SVGElement::removedFrom(rootParent); | |
335 | |
336 if (rootParent->inDocument()) { | |
337 m_fontElement = nullptr; | |
338 document().accessSVGExtensions().unregisterSVGFontFaceElement(this); | |
339 | |
340 // FIXME: HTMLTemplateElement's document or imported document can be ac
tive? | |
341 // If so, we also need to check whether fontSelector() is nullptr or not
. | |
342 // Otherwise, we will use just document().isActive() here. | |
343 if (document().isActive() && document().styleEngine()->fontSelector()) { | |
344 document().styleEngine()->fontSelector()->fontFaceCache()->remove(m_
fontFaceRule.get()); | |
345 document().accessSVGExtensions().registerPendingSVGFontFaceElementsF
orRemoval(this); | |
346 } | |
347 m_fontFaceRule->mutableProperties().clear(); | |
348 document().styleResolverChanged(); | |
349 } else | |
350 ASSERT(!m_fontElement); | |
351 } | |
352 | |
353 void SVGFontFaceElement::childrenChanged(const ChildrenChange& change) | |
354 { | |
355 SVGElement::childrenChanged(change); | |
356 rebuildFontFace(); | |
357 } | |
358 | |
359 void SVGFontFaceElement::trace(Visitor* visitor) | |
360 { | |
361 visitor->trace(m_fontFaceRule); | |
362 visitor->trace(m_fontElement); | |
363 SVGElement::trace(visitor); | |
364 } | |
365 | |
366 } // namespace blink | |
367 | |
368 #endif // ENABLE(SVG_FONTS) | |
OLD | NEW |