/* * 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 "ElementIterator.h" #include "FontCascade.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(SVGElement) END_REGISTER_ANIMATED_PROPERTIES inline SVGFontElement::SVGFontElement(const QualifiedName& tagName, Document& document) : SVGElement(tagName, document) , m_missingGlyph(0) , m_isGlyphCacheValid(false) { ASSERT(hasTagName(SVGNames::fontTag)); registerAnimatedPropertiesForSVGFontElement(); } Ref 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; } const SVGMissingGlyphElement* SVGFontElement::firstMissingGlyphElement() const { return childrenOfType(*this).first(); } 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; for (auto& unicode : ligatures) { unsigned unicodeLength = unicode.length(); ASSERT(unicodeLength > 1); for (unsigned i = 0; i < unicodeLength; ++i) { UChar character = unicode[i]; String lookupString(&character, 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; const SVGMissingGlyphElement* firstMissingGlyphElement = nullptr; Vector ligatures; for (auto& child : childrenOfType(*this)) { if (is(child)) { SVGGlyphElement& glyph = downcast(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 (is(child)) { SVGHKernElement& hkern = downcast(child); SVGKerningPair kerningPair; if (hkern.buildHorizontalKerningPair(kerningPair)) m_horizontalKerningMap.insert(kerningPair); } else if (is(child)) { SVGVKernElement& vkern = downcast(child); SVGKerningPair kerningPair; if (vkern.buildVerticalKerningPair(kerningPair)) m_verticalKerningMap.insert(kerningPair); } else if (is(child) && !firstMissingGlyphElement) firstMissingGlyphElement = &downcast(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; for (auto& name : kerningPair.unicodeName1) { if (unicodeMap.contains(name)) unicodeMap.get(name)->append(svgKerning); else { auto newVector = std::make_unique(); newVector->append(svgKerning); unicodeMap.add(name, WTFMove(newVector)); } } for (auto& name : kerningPair.glyphName1) { if (glyphMap.contains(name)) glyphMap.get(name)->append(svgKerning); else { auto newVector = std::make_unique(); newVector->append(svgKerning); glyphMap.add(name, WTFMove(newVector)); } } 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]; for (auto& range : ranges) { if (firstChar >= range.first && firstChar <= range.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)