cb643871523c4ac2b435c5e1bbcea99b7151f577
[WebKit-https.git] / Source / WebCore / svg / SVGFontElement.cpp
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
37 namespace WebCore {
38
39 // Animated property definitions
40 DEFINE_ANIMATED_BOOLEAN(SVGFontElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
41
42 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGFontElement)
43     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
44     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGElement)
45 END_REGISTER_ANIMATED_PROPERTIES
46
47 inline SVGFontElement::SVGFontElement(const QualifiedName& tagName, Document* document)
48     : SVGElement(tagName, document) 
49     , m_missingGlyph(0)
50     , m_isGlyphCacheValid(false)
51 {
52     ASSERT(hasTagName(SVGNames::fontTag));
53     registerAnimatedPropertiesForSVGFontElement();
54 }
55
56 PassRefPtr<SVGFontElement> SVGFontElement::create(const QualifiedName& tagName, Document* document)
57 {
58     return adoptRef(new SVGFontElement(tagName, document));
59 }
60
61 void 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
71 SVGMissingGlyphElement* SVGFontElement::firstMissingGlyphElement() const
72 {
73     for (Node* child = firstChild(); child; child = child->nextSibling()) {
74         if (child->hasTagName(SVGNames::missing_glyphTag))
75             return toSVGMissingGlyphElement(child);
76     }
77
78     return 0;
79 }
80
81 void SVGFontElement::registerLigaturesInGlyphCache(Vector<String>& ligatures)
82 {
83     ASSERT(!ligatures.isEmpty());
84
85     // Register each character of a ligature in the map, if not present.
86     // Eg. If only a "fi" ligature is present, but not "f" and "i", the
87     // GlyphPage will not contain any entries for "f" and "i", so the
88     // SVGFont is not used to render the text "fi1234". Register an
89     // empty SVGGlyph with the character, so the SVG Font will be used
90     // to render the text. If someone tries to render "f2" the SVG Font
91     // will not be able to find a glyph for "f", but handles the fallback
92     // character substitution properly through glyphDataForCharacter().
93     Vector<SVGGlyph> glyphs;
94     size_t ligaturesSize = ligatures.size();
95     for (size_t i = 0; i < ligaturesSize; ++i) {
96         const String& unicode = ligatures[i];
97
98         unsigned unicodeLength = unicode.length();
99         ASSERT(unicodeLength > 1);
100
101         const UChar* characters = unicode.characters();
102         for (unsigned i = 0; i < unicodeLength; ++i) {
103             String lookupString(characters + i, 1);
104             m_glyphMap.collectGlyphsForString(lookupString, glyphs);
105             if (!glyphs.isEmpty()) {
106                 glyphs.clear();
107                 continue;
108             }
109                 
110             // This glyph is never meant to be used for rendering, only as identifier as a part of a ligature.
111             SVGGlyph newGlyphPart;
112             newGlyphPart.isPartOfLigature = true;
113             m_glyphMap.addGlyph(String(), lookupString, newGlyphPart);
114         }
115     }
116 }
117
118 void SVGFontElement::ensureGlyphCache()
119 {
120     if (m_isGlyphCacheValid)
121         return;
122
123     SVGMissingGlyphElement* firstMissingGlyphElement = 0;
124     Vector<String> ligatures;
125     for (Node* child = firstChild(); child; child = child->nextSibling()) {
126         if (child->hasTagName(SVGNames::glyphTag)) {
127             SVGGlyphElement* glyph = static_cast<SVGGlyphElement*>(child);
128             AtomicString unicode = glyph->fastGetAttribute(SVGNames::unicodeAttr);
129             AtomicString glyphId = glyph->getIdAttribute();
130             if (glyphId.isEmpty() && unicode.isEmpty())
131                 continue;
132
133             m_glyphMap.addGlyph(glyphId, unicode, glyph->buildGlyphIdentifier());
134
135             // Register ligatures, if needed, don't mix up with surrogate pairs though!
136             if (unicode.length() > 1 && !U16_IS_SURROGATE(unicode[0]))
137                 ligatures.append(unicode.string());
138         } else if (child->hasTagName(SVGNames::hkernTag)) {
139             SVGHKernElement* hkern = static_cast<SVGHKernElement*>(child);
140             hkern->buildHorizontalKerningPair(m_horizontalKerningPairs);
141         } else if (child->hasTagName(SVGNames::vkernTag)) {
142             SVGVKernElement* vkern = static_cast<SVGVKernElement*>(child);
143             vkern->buildVerticalKerningPair(m_verticalKerningPairs);
144         } else if (child->hasTagName(SVGNames::missing_glyphTag) && !firstMissingGlyphElement)
145             firstMissingGlyphElement = toSVGMissingGlyphElement(child);
146     }
147
148     // Register each character of each ligature, if needed.
149     if (!ligatures.isEmpty())
150         registerLigaturesInGlyphCache(ligatures);
151
152     // Register missing-glyph element, if present.
153     if (firstMissingGlyphElement) {
154         SVGGlyph svgGlyph = SVGGlyphElement::buildGenericGlyphIdentifier(firstMissingGlyphElement);
155         m_glyphMap.appendToGlyphTable(svgGlyph);
156         m_missingGlyph = svgGlyph.tableEntry;
157         ASSERT(m_missingGlyph > 0);
158     }
159
160     m_isGlyphCacheValid = true;
161 }
162
163 static bool stringMatchesUnicodeRange(const String& unicodeString, const UnicodeRanges& ranges, const HashSet<String>& unicodeValues)
164 {
165     if (unicodeString.isEmpty())
166         return false;
167
168     if (!ranges.isEmpty()) {
169         UChar firstChar = unicodeString[0];
170         const UnicodeRanges::const_iterator end = ranges.end();
171         for (UnicodeRanges::const_iterator it = ranges.begin(); it != end; ++it) {
172             if (firstChar >= it->first && firstChar <= it->second)
173                 return true;
174         }
175     }
176
177     if (!unicodeValues.isEmpty())
178         return unicodeValues.contains(unicodeString);
179     
180     return false;
181 }
182
183 static bool stringMatchesGlyphName(const String& glyphName, const HashSet<String>& glyphValues)
184 {
185     if (glyphName.isEmpty())
186         return false;
187
188     if (!glyphValues.isEmpty())
189         return glyphValues.contains(glyphName);
190     
191     return false;
192 }
193
194 static bool matches(const String& u1, const String& g1, const String& u2, const String& g2, const SVGKerningPair& kerningPair)
195 {
196     if (!stringMatchesUnicodeRange(u1, kerningPair.unicodeRange1, kerningPair.unicodeName1)
197         && !stringMatchesGlyphName(g1, kerningPair.glyphName1))
198         return false;
199
200     if (!stringMatchesUnicodeRange(u2, kerningPair.unicodeRange2, kerningPair.unicodeName2)
201         && !stringMatchesGlyphName(g2, kerningPair.glyphName2))
202         return false;
203
204     return true;
205 }
206
207 static float kerningForPairOfStringsAndGlyphs(const KerningPairVector& kerningPairs, const String& u1, const String& g1, const String& u2, const String& g2)
208 {
209     KerningPairVector::const_iterator it = kerningPairs.end() - 1;
210     const KerningPairVector::const_iterator begin = kerningPairs.begin() - 1;
211     for (; it != begin; --it) {
212         if (matches(u1, g1, u2, g2, *it))
213             return it->kerning;
214     }
215
216     return 0;
217 }
218     
219 float SVGFontElement::horizontalKerningForPairOfStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2) const
220 {
221     if (m_horizontalKerningPairs.isEmpty())
222         return 0;
223
224     return kerningForPairOfStringsAndGlyphs(m_horizontalKerningPairs, u1, g1, u2, g2);
225 }
226
227 float SVGFontElement::verticalKerningForPairOfStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2) const
228 {
229     if (m_verticalKerningPairs.isEmpty())
230         return 0;
231
232     return kerningForPairOfStringsAndGlyphs(m_verticalKerningPairs, u1, g1, u2, g2);
233 }
234
235 void SVGFontElement::collectGlyphsForString(const String& string, Vector<SVGGlyph>& glyphs)
236 {
237     ensureGlyphCache();
238     m_glyphMap.collectGlyphsForString(string, glyphs);
239 }
240
241 void SVGFontElement::collectGlyphsForGlyphName(const String& glyphName, Vector<SVGGlyph>& glyphs)
242 {
243     ensureGlyphCache();
244     // FIXME: We only support glyphName -> single glyph mapping so far.
245     glyphs.append(m_glyphMap.glyphIdentifierForGlyphName(glyphName));
246 }
247
248 SVGGlyph SVGFontElement::svgGlyphForGlyph(Glyph glyph)
249 {
250     ensureGlyphCache();
251     return m_glyphMap.svgGlyphForGlyph(glyph);
252 }
253     
254 Glyph SVGFontElement::missingGlyph()
255 {
256     ensureGlyphCache();
257     return m_missingGlyph;
258 }
259
260 }
261
262 #endif // ENABLE(SVG_FONTS)