1/*
2 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
3 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
4 * Copyright (C) Research In Motion Limited 2010. 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 "SVGFontElement.h"
26
27#include "Document.h"
28#include "Font.h"
29#include "GlyphPageTreeNode.h"
30#include "SVGGlyphElement.h"
31#include "SVGHKernElement.h"
32#include "SVGMissingGlyphElement.h"
33#include "SVGNames.h"
34#include "SVGVKernElement.h"
35#include <wtf/ASCIICType.h>
36
37namespace WebCore {
38
39// Animated property declarations
40DEFINE_ANIMATED_BOOLEAN(SVGFontElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
41
42inline SVGFontElement::SVGFontElement(const QualifiedName& tagName, Document* document)
43    : SVGStyledElement(tagName, document)
44    , m_isGlyphCacheValid(false)
45{
46}
47
48PassRefPtr<SVGFontElement> SVGFontElement::create(const QualifiedName& tagName, Document* document)
49{
50    return adoptRef(new SVGFontElement(tagName, document));
51}
52
53void SVGFontElement::synchronizeProperty(const QualifiedName& attrName)
54{
55    SVGStyledElement::synchronizeProperty(attrName);
56
57    if (attrName == anyQName() || SVGExternalResourcesRequired::isKnownAttribute(attrName))
58        synchronizeExternalResourcesRequired();
59}
60
61void SVGFontElement::invalidateGlyphCache()
62{
63    if (m_isGlyphCacheValid) {
64        m_glyphMap.clear();
65        m_horizontalKerningPairs.clear();
66        m_verticalKerningPairs.clear();
67    }
68    m_isGlyphCacheValid = false;
69}
70
71SVGMissingGlyphElement* SVGFontElement::firstMissingGlyphElement() const
72{
73    for (Node* child = firstChild(); child; child = child->nextSibling()) {
74        if (child->hasTagName(SVGNames::missing_glyphTag))
75            return static_cast<SVGMissingGlyphElement*>(child);
76    }
77
78    return 0;
79}
80
81void SVGFontElement::ensureGlyphCache() const
82{
83    if (m_isGlyphCacheValid)
84        return;
85
86    for (Node* child = firstChild(); child; child = child->nextSibling()) {
87        if (child->hasTagName(SVGNames::glyphTag)) {
88            SVGGlyphElement* glyph = static_cast<SVGGlyphElement*>(child);
89            String unicode = glyph->getAttribute(SVGNames::unicodeAttr);
90            if (unicode.length())
91                m_glyphMap.add(unicode, glyph->buildGlyphIdentifier());
92        } else if (child->hasTagName(SVGNames::hkernTag)) {
93            SVGHKernElement* hkern = static_cast<SVGHKernElement*>(child);
94            hkern->buildHorizontalKerningPair(m_horizontalKerningPairs);
95        } else if (child->hasTagName(SVGNames::vkernTag)) {
96            SVGVKernElement* vkern = static_cast<SVGVKernElement*>(child);
97            vkern->buildVerticalKerningPair(m_verticalKerningPairs);
98        }
99    }
100
101    m_isGlyphCacheValid = true;
102}
103
104static bool stringMatchesUnicodeRange(const String& unicodeString, const UnicodeRanges& ranges, const HashSet<String>& unicodeValues)
105{
106    if (unicodeString.isEmpty())
107        return false;
108
109    if (!ranges.isEmpty()) {
110        UChar firstChar = unicodeString[0];
111        const UnicodeRanges::const_iterator end = ranges.end();
112        for (UnicodeRanges::const_iterator it = ranges.begin(); it != end; ++it) {
113            if (firstChar >= it->first && firstChar <= it->second)
114                return true;
115        }
116    }
117
118    if (!unicodeValues.isEmpty())
119        return unicodeValues.contains(unicodeString);
120
121    return false;
122}
123
124static bool stringMatchesGlyphName(const String& glyphName, const HashSet<String>& glyphValues)
125{
126    if (glyphName.isEmpty())
127        return false;
128
129    if (!glyphValues.isEmpty())
130        return glyphValues.contains(glyphName);
131
132    return false;
133}
134
135static bool matches(const String& u1, const String& g1, const String& u2, const String& g2, const SVGKerningPair& kerningPair)
136{
137    if (!stringMatchesUnicodeRange(u1, kerningPair.unicodeRange1, kerningPair.unicodeName1)
138        && !stringMatchesGlyphName(g1, kerningPair.glyphName1))
139        return false;
140
141    if (!stringMatchesUnicodeRange(u2, kerningPair.unicodeRange2, kerningPair.unicodeName2)
142        && !stringMatchesGlyphName(g2, kerningPair.glyphName2))
143        return false;
144
145    return true;
146}
147
148static float kerningForPairOfStringsAndGlyphs(KerningPairVector& kerningPairs, const String& u1, const String& g1, const String& u2, const String& g2)
149{
150    KerningPairVector::const_iterator it = kerningPairs.end() - 1;
151    const KerningPairVector::const_iterator begin = kerningPairs.begin() - 1;
152    for (; it != begin; --it) {
153        if (matches(u1, g1, u2, g2, *it))
154            return it->kerning;
155    }
156
157    return 0.0f;
158}
159
160float SVGFontElement::horizontalKerningForPairOfStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2) const
161{
162    if (m_horizontalKerningPairs.isEmpty())
163        return 0.0f;
164
165    return kerningForPairOfStringsAndGlyphs(m_horizontalKerningPairs, u1, g1, u2, g2);
166}
167
168float SVGFontElement::verticalKerningForPairOfStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2) const
169{
170    if (m_verticalKerningPairs.isEmpty())
171        return 0.0f;
172
173    return kerningForPairOfStringsAndGlyphs(m_verticalKerningPairs, u1, g1, u2, g2);
174}
175
176void SVGFontElement::getGlyphIdentifiersForString(const String& string, Vector<SVGGlyphIdentifier>& glyphs) const
177{
178    ensureGlyphCache();
179    m_glyphMap.get(string, glyphs);
180}
181
182AttributeToPropertyTypeMap& SVGFontElement::attributeToPropertyTypeMap()
183{
184    DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ());
185    return s_attributeToPropertyTypeMap;
186}
187
188void SVGFontElement::fillAttributeToPropertyTypeMap()
189{
190    SVGStyledElement::fillPassedAttributeToPropertyTypeMap(attributeToPropertyTypeMap());
191}
192
193}
194
195#endif // ENABLE(SVG_FONTS)
196