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