| /* |
| Copyright (C) 2007 Eric Seidel <eric@webkit.org> |
| Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Library General Public |
| License as published by the Free Software Foundation; either |
| version 2 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Library General Public License for more details. |
| |
| You should have received a copy of the GNU Library General Public License |
| along with this library; see the file COPYING.LIB. If not, write to |
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| |
| #if ENABLE(SVG_FONTS) |
| #include "SVGFontElement.h" |
| |
| #include "Document.h" |
| #include "Font.h" |
| #include "GlyphPageTreeNode.h" |
| #include "SVGGlyphElement.h" |
| #include "SVGMissingGlyphElement.h" |
| #include "SVGNames.h" |
| #include "SVGParserUtilities.h" |
| #include <wtf/ASCIICType.h> |
| |
| using namespace WTF; |
| |
| namespace WebCore { |
| |
| using namespace SVGNames; |
| |
| SVGFontElement::SVGFontElement(const QualifiedName& tagName, Document* doc) |
| : SVGStyledElement(tagName, doc) |
| , m_isGlyphCacheValid(false) |
| { |
| } |
| |
| SVGFontElement::~SVGFontElement() |
| { |
| } |
| |
| void SVGFontElement::invalidateGlyphCache() |
| { |
| if (m_isGlyphCacheValid) { |
| m_glyphMap.clear(); |
| m_kerningPairs.clear(); |
| } |
| m_isGlyphCacheValid = false; |
| } |
| |
| SVGMissingGlyphElement* SVGFontElement::firstMissingGlyphElement() const |
| { |
| for (Node* child = firstChild(); child; child = child->nextSibling()) { |
| if (child->hasTagName(missing_glyphTag)) |
| return static_cast<SVGMissingGlyphElement*>(child); |
| } |
| |
| return 0; |
| } |
| |
| void SVGFontElement::ensureGlyphCache() const |
| { |
| if (m_isGlyphCacheValid) |
| return; |
| |
| for (Node* child = firstChild(); child; child = child->nextSibling()) { |
| if (child->hasTagName(glyphTag)) { |
| SVGGlyphElement* glyph = static_cast<SVGGlyphElement*>(child); |
| String unicode = glyph->getAttribute(unicodeAttr); |
| if (unicode.length()) |
| m_glyphMap.add(unicode, glyph->buildGlyphIdentifier()); |
| } else if (child->hasTagName(hkernTag)) { |
| SVGHKernElement* hkern = static_cast<SVGHKernElement*>(child); |
| SVGHorizontalKerningPair kerningPair = hkern->buildHorizontalKerningPair(); |
| m_kerningPairs.append(kerningPair); |
| } |
| } |
| |
| m_isGlyphCacheValid = true; |
| } |
| |
| // Returns the number of characters consumed or 0 if no range was found. |
| static unsigned parseUnicodeRange(const UChar* characters, unsigned length, pair<unsigned, unsigned>& range) |
| { |
| if (length < 2) |
| return 0; |
| if (characters[0] != 'U') |
| return 0; |
| if (characters[1] != '+') |
| return 0; |
| |
| // Parse the starting hex number (or its prefix). |
| unsigned start = 0; |
| unsigned startLength = 0; |
| for (unsigned i = 2; i < length; ++i) { |
| if (!isASCIIHexDigit(characters[i])) |
| break; |
| if (++startLength > 6) |
| return 0; |
| start = (start << 4) | toASCIIHexValue(characters[i]); |
| } |
| |
| // Handle the case of ranges separated by "-" sign. |
| if (2 + startLength < length && characters[2 + startLength] == '-') { |
| if (!startLength) |
| return 0; |
| |
| // Parse the ending hex number (or its prefix). |
| unsigned end = 0; |
| unsigned endLength = 0; |
| for (unsigned i = 2 + startLength + 1; i < length; ++i) { |
| if (!isASCIIHexDigit(characters[i])) |
| break; |
| if (++endLength > 6) |
| return 0; |
| end = (end << 4) | toASCIIHexValue(characters[i]); |
| } |
| |
| if (!endLength) |
| return 0; |
| |
| range.first = start; |
| range.second = end; |
| return 2 + startLength + 1 + endLength; |
| } |
| |
| // Handle the case of a number with some optional trailing question marks. |
| unsigned end = start; |
| for (unsigned i = 2 + startLength; i < length; ++i) { |
| if (characters[i] != '?') |
| break; |
| if (++startLength > 6) |
| return 0; |
| start <<= 4; |
| end = (end << 4) | 0xF; |
| } |
| |
| if (!startLength) |
| return 0; |
| |
| range.first = start; |
| range.second = end; |
| return 2 + startLength; |
| } |
| |
| static bool parseUnicodeRangeList(const UChar* characters, unsigned length, Vector<pair<unsigned, unsigned> >& ranges) |
| { |
| ranges.clear(); |
| if (!length) |
| return true; |
| |
| const UChar* remainingCharacters = characters; |
| unsigned remainingLength = length; |
| |
| while (1) { |
| pair<unsigned, unsigned> range; |
| unsigned charactersConsumed = parseUnicodeRange(remainingCharacters, remainingLength, range); |
| if (charactersConsumed) { |
| ranges.append(range); |
| remainingCharacters += charactersConsumed; |
| remainingLength -= charactersConsumed; |
| } else { |
| if (!remainingLength) |
| return false; |
| UChar character = remainingCharacters[0]; |
| if (character == ',') |
| return false; |
| ranges.append(make_pair(character, character)); |
| ++remainingCharacters; |
| --remainingLength; |
| } |
| if (!remainingLength) |
| return true; |
| if (remainingCharacters[0] != ',') |
| return false; |
| ++remainingCharacters; |
| --remainingLength; |
| } |
| } |
| |
| static bool stringMatchesUnicodeRange(const String& unicodeString, const String& unicodeRangeSpec) |
| { |
| Vector<pair<unsigned, unsigned> > ranges; |
| if (!parseUnicodeRangeList(unicodeRangeSpec.characters(), unicodeRangeSpec.length(), ranges)) |
| return false; |
| |
| if (unicodeString.length() != ranges.size()) |
| return false; |
| |
| for (size_t i = 0; i < unicodeString.length(); ++i) { |
| UChar c = unicodeString[i]; |
| if (c < ranges[i].first || c > ranges[i].second) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool matches(const String& u1, const String& g1, const String& u2, const String& g2, const SVGHorizontalKerningPair& kerningPair) |
| { |
| if (kerningPair.unicode1.length() && !stringMatchesUnicodeRange(u1, kerningPair.unicode1)) |
| return false; |
| if (kerningPair.glyphName1.length() && kerningPair.glyphName1 != g1) |
| return false; |
| |
| if (kerningPair.unicode2.length() && !stringMatchesUnicodeRange(u2, kerningPair.unicode2)) |
| return false; |
| if (kerningPair.glyphName2.length() && kerningPair.glyphName2 != g2) |
| return false; |
| |
| return true; |
| } |
| |
| bool SVGFontElement::getHorizontalKerningPairForStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2, SVGHorizontalKerningPair& kerningPair) const |
| { |
| for (size_t i = 0; i < m_kerningPairs.size(); ++i) { |
| if (matches(u1, g1, u2, g2, m_kerningPairs[i])) { |
| kerningPair = m_kerningPairs[i]; |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| void SVGFontElement::getGlyphIdentifiersForString(const String& string, Vector<SVGGlyphIdentifier>& glyphs) const |
| { |
| ensureGlyphCache(); |
| m_glyphMap.get(string, glyphs); |
| } |
| |
| } |
| |
| #endif // ENABLE(SVG_FONTS) |