/* * Copyright (C) 2007 Eric Seidel * Copyright (C) 2007 Nikolas Zimmermann * Copyright (C) Research In Motion Limited 2010. All rights reserved. * * 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 "SVGHKernElement.h" #include "SVGMissingGlyphElement.h" #include "SVGNames.h" #include "SVGVKernElement.h" #include namespace WebCore { // Animated property definitions DEFINE_ANIMATED_BOOLEAN(SVGFontElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGFontElement) REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired) REGISTER_PARENT_ANIMATED_PROPERTIES(SVGStyledElement) END_REGISTER_ANIMATED_PROPERTIES inline SVGFontElement::SVGFontElement(const QualifiedName& tagName, Document* document) : SVGStyledElement(tagName, document) , m_missingGlyph(0) , m_isGlyphCacheValid(false) { ASSERT(hasTagName(SVGNames::fontTag)); registerAnimatedPropertiesForSVGFontElement(); } PassRefPtr SVGFontElement::create(const QualifiedName& tagName, Document* document) { return adoptRef(new SVGFontElement(tagName, document)); } void SVGFontElement::invalidateGlyphCache() { if (m_isGlyphCacheValid) { m_glyphMap.clear(); m_horizontalKerningMap.clear(); m_verticalKerningMap.clear(); } m_isGlyphCacheValid = false; } SVGMissingGlyphElement* SVGFontElement::firstMissingGlyphElement() const { for (Node* child = firstChild(); child; child = child->nextSibling()) { if (child->hasTagName(SVGNames::missing_glyphTag)) return static_cast(child); } return 0; } void SVGFontElement::registerLigaturesInGlyphCache(Vector& ligatures) { ASSERT(!ligatures.isEmpty()); // Register each character of a ligature in the map, if not present. // Eg. If only a "fi" ligature is present, but not "f" and "i", the // GlyphPage will not contain any entries for "f" and "i", so the // SVGFont is not used to render the text "fi1234". Register an // empty SVGGlyph with the character, so the SVG Font will be used // to render the text. If someone tries to render "f2" the SVG Font // will not be able to find a glyph for "f", but handles the fallback // character substitution properly through glyphDataForCharacter(). Vector glyphs; size_t ligaturesSize = ligatures.size(); for (size_t i = 0; i < ligaturesSize; ++i) { const String& unicode = ligatures[i]; unsigned unicodeLength = unicode.length(); ASSERT(unicodeLength > 1); const UChar* characters = unicode.characters(); for (unsigned i = 0; i < unicodeLength; ++i) { String lookupString(characters + i, 1); m_glyphMap.collectGlyphsForString(lookupString, glyphs); if (!glyphs.isEmpty()) { glyphs.clear(); continue; } // This glyph is never meant to be used for rendering, only as identifier as a part of a ligature. SVGGlyph newGlyphPart; newGlyphPart.isPartOfLigature = true; m_glyphMap.addGlyph(String(), lookupString, newGlyphPart); } } } void SVGFontElement::ensureGlyphCache() { if (m_isGlyphCacheValid) return; SVGMissingGlyphElement* firstMissingGlyphElement = 0; Vector ligatures; for (Node* child = firstChild(); child; child = child->nextSibling()) { if (child->hasTagName(SVGNames::glyphTag)) { SVGGlyphElement* glyph = static_cast(child); AtomicString unicode = glyph->fastGetAttribute(SVGNames::unicodeAttr); AtomicString glyphId = glyph->getIdAttribute(); if (glyphId.isEmpty() && unicode.isEmpty()) continue; m_glyphMap.addGlyph(glyphId, unicode, glyph->buildGlyphIdentifier()); // Register ligatures, if needed, don't mix up with surrogate pairs though! if (unicode.length() > 1 && !U16_IS_SURROGATE(unicode[0])) ligatures.append(unicode.string()); } else if (child->hasTagName(SVGNames::hkernTag)) { SVGHKernElement* hkern = static_cast(child); hkern->buildHorizontalKerningPair(m_horizontalKerningMap); } else if (child->hasTagName(SVGNames::vkernTag)) { SVGVKernElement* vkern = static_cast(child); vkern->buildVerticalKerningPair(m_verticalKerningMap); } else if (child->hasTagName(SVGNames::missing_glyphTag) && !firstMissingGlyphElement) firstMissingGlyphElement = static_cast(child); } // Register each character of each ligature, if needed. if (!ligatures.isEmpty()) registerLigaturesInGlyphCache(ligatures); // Register missing-glyph element, if present. if (firstMissingGlyphElement) { SVGGlyph svgGlyph = SVGGlyphElement::buildGenericGlyphIdentifier(firstMissingGlyphElement); m_glyphMap.appendToGlyphTable(svgGlyph); m_missingGlyph = svgGlyph.tableEntry; ASSERT(m_missingGlyph > 0); } m_isGlyphCacheValid = true; } void SVGKerningMap::clear() { unicodeMap.clear(); glyphMap.clear(); kerningUnicodeRangeMap.clear(); } void SVGKerningMap::insert(const SVGKerningPair& kerningPair) { SVGKerning svgKerning; svgKerning.kerning = kerningPair.kerning; svgKerning.unicodeRange2 = kerningPair.unicodeRange2; svgKerning.unicodeName2 = kerningPair.unicodeName2; svgKerning.glyphName2 = kerningPair.glyphName2; HashSet::const_iterator uIt = kerningPair.unicodeName1.begin(); const HashSet::const_iterator uEnd = kerningPair.unicodeName1.end(); for (; uIt != uEnd; ++uIt) { if (unicodeMap.contains(*uIt)) unicodeMap.get(*uIt)->append(svgKerning); else { OwnPtr newVector = adoptPtr(new SVGKerningVector); newVector->append(svgKerning); unicodeMap.add(*uIt, newVector.release()); } } HashSet::const_iterator gIt = kerningPair.glyphName1.begin(); const HashSet::const_iterator gEnd = kerningPair.glyphName1.end(); for (; gIt != gEnd; ++gIt) { if (glyphMap.contains(*gIt)) glyphMap.get(*gIt)->append(svgKerning); else { OwnPtr newVector = adoptPtr(new SVGKerningVector); newVector->append(svgKerning); glyphMap.add(*gIt, newVector.release()); } } if (!kerningPair.unicodeRange1.isEmpty()) kerningUnicodeRangeMap.append(kerningPair); } static inline bool stringMatchesUnicodeRange(const String& unicodeString, const UnicodeRanges& ranges) { if (unicodeString.isEmpty()) return false; if (!ranges.isEmpty()) { UChar firstChar = unicodeString[0]; const UnicodeRanges::const_iterator end = ranges.end(); for (UnicodeRanges::const_iterator it = ranges.begin(); it != end; ++it) { if (firstChar >= it->first && firstChar <= it->second) return true; } } return false; } static inline bool stringMatchesGlyphName(const String& glyphName, const HashSet& glyphValues) { if (glyphName.isEmpty()) return false; return glyphValues.contains(glyphName); } static inline bool stringMatchesUnicodeName(const String& unicodeName, const HashSet& unicodeValues) { if (unicodeName.isEmpty()) return false; return unicodeValues.contains(unicodeName); } static inline bool matches(const String& u2, const String& g2, const SVGKerning& svgKerning) { return stringMatchesGlyphName(g2, svgKerning.glyphName2) || stringMatchesUnicodeName(u2, svgKerning.unicodeName2) || stringMatchesUnicodeRange(u2, svgKerning.unicodeRange2); } static inline bool matches(const String& u1, const String& u2, const String& g2, const SVGKerningPair& svgKerningPair) { return stringMatchesUnicodeRange(u1, svgKerningPair.unicodeRange1) && matches(u2, g2, svgKerningPair); } static inline float kerningForPairOfStringsAndGlyphs(const SVGKerningMap& kerningMap, const String& u1, const String& g1, const String& u2, const String& g2) { if (!g1.isEmpty() && kerningMap.glyphMap.contains(g1)) { SVGKerningVector* kerningVector = kerningMap.glyphMap.get(g1); SVGKerningVector::const_iterator it = kerningVector->end() - 1; const SVGKerningVector::const_iterator begin = kerningVector->begin() - 1; for (; it != begin; --it) { if (matches(u2, g2, *it)) return it->kerning; } } if (!u1.isEmpty()) { if (kerningMap.unicodeMap.contains(u1)) { SVGKerningVector* kerningVector = kerningMap.unicodeMap.get(u1); SVGKerningVector::const_iterator it = kerningVector->end() - 1; const SVGKerningVector::const_iterator begin = kerningVector->begin() - 1; for (; it != begin; --it) { if (matches(u2, g2, *it)) return it->kerning; } } if (!kerningMap.kerningUnicodeRangeMap.isEmpty()) { Vector::const_iterator it = kerningMap.kerningUnicodeRangeMap.end() - 1; const Vector::const_iterator begin = kerningMap.kerningUnicodeRangeMap.begin() - 1; for (; it != begin; --it) { if (matches(u1, u2, g2, *it)) return it->kerning; } } } return 0; } float SVGFontElement::horizontalKerningForPairOfStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2) const { if (m_horizontalKerningMap.isEmpty()) return 0; return kerningForPairOfStringsAndGlyphs(m_horizontalKerningMap, u1, g1, u2, g2); } float SVGFontElement::verticalKerningForPairOfStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2) const { if (m_verticalKerningMap.isEmpty()) return 0; return kerningForPairOfStringsAndGlyphs(m_verticalKerningMap, u1, g1, u2, g2); } void SVGFontElement::collectGlyphsForString(const String& string, Vector& glyphs) { ensureGlyphCache(); m_glyphMap.collectGlyphsForString(string, glyphs); } void SVGFontElement::collectGlyphsForGlyphName(const String& glyphName, Vector& glyphs) { ensureGlyphCache(); // FIXME: We only support glyphName -> single glyph mapping so far. glyphs.append(m_glyphMap.glyphIdentifierForGlyphName(glyphName)); } SVGGlyph SVGFontElement::svgGlyphForGlyph(Glyph glyph) { ensureGlyphCache(); return m_glyphMap.svgGlyphForGlyph(glyph); } Glyph SVGFontElement::missingGlyph() { ensureGlyphCache(); return m_missingGlyph; } } #endif // ENABLE(SVG_FONTS)