Home | History | Annotate | Download | only in svg
      1 /*
      2    Copyright (C) 2007 Eric Seidel <eric (at) webkit.org>
      3    Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann (at) kde.org>
      4    Copyright (C) 2008 Apple Inc. 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 "SVGFontFaceElement.h"
     26 
     27 #include "CString.h"
     28 #include "CSSFontFaceRule.h"
     29 #include "CSSFontFaceSrcValue.h"
     30 #include "CSSParser.h"
     31 #include "CSSProperty.h"
     32 #include "CSSPropertyNames.h"
     33 #include "CSSStyleSelector.h"
     34 #include "CSSStyleSheet.h"
     35 #include "CSSValueKeywords.h"
     36 #include "CSSValueList.h"
     37 #include "Document.h"
     38 #include "Font.h"
     39 #include "MappedAttribute.h"
     40 #include "SVGFontElement.h"
     41 #include "SVGFontFaceSrcElement.h"
     42 #include "SVGGlyphElement.h"
     43 #include "SVGNames.h"
     44 #include <math.h>
     45 
     46 namespace WebCore {
     47 
     48 using namespace SVGNames;
     49 
     50 SVGFontFaceElement::SVGFontFaceElement(const QualifiedName& tagName, Document* doc)
     51     : SVGElement(tagName, doc)
     52     , m_fontFaceRule(CSSFontFaceRule::create())
     53     , m_styleDeclaration(CSSMutableStyleDeclaration::create())
     54 {
     55     m_styleDeclaration->setParent(document()->mappedElementSheet());
     56     m_styleDeclaration->setStrictParsing(true);
     57     m_fontFaceRule->setDeclaration(m_styleDeclaration.get());
     58 }
     59 
     60 SVGFontFaceElement::~SVGFontFaceElement()
     61 {
     62     removeFromMappedElementSheet();
     63 }
     64 
     65 static int cssPropertyIdForSVGAttributeName(const QualifiedName& attrName)
     66 {
     67     if (!attrName.namespaceURI().isNull())
     68         return 0;
     69 
     70     static HashMap<AtomicStringImpl*, int>* propertyNameToIdMap = 0;
     71     if (!propertyNameToIdMap) {
     72         propertyNameToIdMap = new HashMap<AtomicStringImpl*, int>;
     73         // This is a list of all @font-face CSS properties which are exposed as SVG XML attributes
     74         // Those commented out are not yet supported by WebCore's style system
     75         //mapAttributeToCSSProperty(propertyNameToIdMap, accent_heightAttr);
     76         //mapAttributeToCSSProperty(propertyNameToIdMap, alphabeticAttr);
     77         //mapAttributeToCSSProperty(propertyNameToIdMap, ascentAttr);
     78         //mapAttributeToCSSProperty(propertyNameToIdMap, bboxAttr);
     79         //mapAttributeToCSSProperty(propertyNameToIdMap, cap_heightAttr);
     80         //mapAttributeToCSSProperty(propertyNameToIdMap, descentAttr);
     81         mapAttributeToCSSProperty(propertyNameToIdMap, font_familyAttr);
     82         mapAttributeToCSSProperty(propertyNameToIdMap, font_sizeAttr);
     83         mapAttributeToCSSProperty(propertyNameToIdMap, font_stretchAttr);
     84         mapAttributeToCSSProperty(propertyNameToIdMap, font_styleAttr);
     85         mapAttributeToCSSProperty(propertyNameToIdMap, font_variantAttr);
     86         mapAttributeToCSSProperty(propertyNameToIdMap, font_weightAttr);
     87         //mapAttributeToCSSProperty(propertyNameToIdMap, hangingAttr);
     88         //mapAttributeToCSSProperty(propertyNameToIdMap, ideographicAttr);
     89         //mapAttributeToCSSProperty(propertyNameToIdMap, mathematicalAttr);
     90         //mapAttributeToCSSProperty(propertyNameToIdMap, overline_positionAttr);
     91         //mapAttributeToCSSProperty(propertyNameToIdMap, overline_thicknessAttr);
     92         //mapAttributeToCSSProperty(propertyNameToIdMap, panose_1Attr);
     93         //mapAttributeToCSSProperty(propertyNameToIdMap, slopeAttr);
     94         //mapAttributeToCSSProperty(propertyNameToIdMap, stemhAttr);
     95         //mapAttributeToCSSProperty(propertyNameToIdMap, stemvAttr);
     96         //mapAttributeToCSSProperty(propertyNameToIdMap, strikethrough_positionAttr);
     97         //mapAttributeToCSSProperty(propertyNameToIdMap, strikethrough_thicknessAttr);
     98         //mapAttributeToCSSProperty(propertyNameToIdMap, underline_positionAttr);
     99         //mapAttributeToCSSProperty(propertyNameToIdMap, underline_thicknessAttr);
    100         //mapAttributeToCSSProperty(propertyNameToIdMap, unicode_rangeAttr);
    101         //mapAttributeToCSSProperty(propertyNameToIdMap, units_per_emAttr);
    102         //mapAttributeToCSSProperty(propertyNameToIdMap, v_alphabeticAttr);
    103         //mapAttributeToCSSProperty(propertyNameToIdMap, v_hangingAttr);
    104         //mapAttributeToCSSProperty(propertyNameToIdMap, v_ideographicAttr);
    105         //mapAttributeToCSSProperty(propertyNameToIdMap, v_mathematicalAttr);
    106         //mapAttributeToCSSProperty(propertyNameToIdMap, widthsAttr);
    107         //mapAttributeToCSSProperty(propertyNameToIdMap, x_heightAttr);
    108     }
    109 
    110     return propertyNameToIdMap->get(attrName.localName().impl());
    111 }
    112 
    113 void SVGFontFaceElement::parseMappedAttribute(MappedAttribute* attr)
    114 {
    115     int propId = cssPropertyIdForSVGAttributeName(attr->name());
    116     if (propId > 0) {
    117         m_styleDeclaration->setProperty(propId, attr->value(), false);
    118         if (inDocument())
    119             rebuildFontFace();
    120         return;
    121     }
    122 
    123     SVGElement::parseMappedAttribute(attr);
    124 }
    125 
    126 unsigned SVGFontFaceElement::unitsPerEm() const
    127 {
    128     const AtomicString& value = getAttribute(units_per_emAttr);
    129     if (value.isEmpty())
    130         return defaultUnitsPerEm;
    131 
    132     return static_cast<unsigned>(ceilf(value.toFloat()));
    133 }
    134 
    135 int SVGFontFaceElement::xHeight() const
    136 {
    137     return static_cast<int>(ceilf(getAttribute(x_heightAttr).toFloat()));
    138 }
    139 
    140 float SVGFontFaceElement::horizontalOriginX() const
    141 {
    142     if (!m_fontElement)
    143         return 0.0f;
    144 
    145     // Spec: The X-coordinate in the font coordinate system of the origin of a glyph to be used when
    146     // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.)
    147     // If the attribute is not specified, the effect is as if a value of "0" were specified.
    148     return m_fontElement->getAttribute(horiz_origin_xAttr).toFloat();
    149 }
    150 
    151 float SVGFontFaceElement::horizontalOriginY() const
    152 {
    153     if (!m_fontElement)
    154         return 0.0f;
    155 
    156     // Spec: The Y-coordinate in the font coordinate system of the origin of a glyph to be used when
    157     // drawing horizontally oriented text. (Note that the origin applies to all glyphs in the font.)
    158     // If the attribute is not specified, the effect is as if a value of "0" were specified.
    159     return m_fontElement->getAttribute(horiz_origin_yAttr).toFloat();
    160 }
    161 
    162 float SVGFontFaceElement::horizontalAdvanceX() const
    163 {
    164     if (!m_fontElement)
    165         return 0.0f;
    166 
    167     // Spec: The default horizontal advance after rendering a glyph in horizontal orientation. Glyph
    168     // widths are required to be non-negative, even if the glyph is typically rendered right-to-left,
    169     // as in Hebrew and Arabic scripts.
    170     return m_fontElement->getAttribute(horiz_adv_xAttr).toFloat();
    171 }
    172 
    173 float SVGFontFaceElement::verticalOriginX() const
    174 {
    175     if (!m_fontElement)
    176         return 0.0f;
    177 
    178     // Spec: The default X-coordinate in the font coordinate system of the origin of a glyph to be used when
    179     // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute
    180     // were set to half of the effective value of attribute horiz-adv-x.
    181     const AtomicString& value = m_fontElement->getAttribute(vert_origin_xAttr);
    182     if (value.isEmpty())
    183         return horizontalAdvanceX() / 2.0f;
    184 
    185     return value.toFloat();
    186 }
    187 
    188 float SVGFontFaceElement::verticalOriginY() const
    189 {
    190     if (!m_fontElement)
    191         return 0.0f;
    192 
    193     // Spec: The default Y-coordinate in the font coordinate system of the origin of a glyph to be used when
    194     // drawing vertically oriented text. If the attribute is not specified, the effect is as if the attribute
    195     // were set to the position specified by the font's ascent attribute.
    196     const AtomicString& value = m_fontElement->getAttribute(vert_origin_yAttr);
    197     if (value.isEmpty())
    198         return ascent();
    199 
    200     return value.toFloat();
    201 }
    202 
    203 float SVGFontFaceElement::verticalAdvanceY() const
    204 {
    205     if (!m_fontElement)
    206         return 0.0f;
    207 
    208     // Spec: The default vertical advance after rendering a glyph in vertical orientation. If the attribute is
    209     // not specified, the effect is as if a value equivalent of one em were specified (see units-per-em).
    210     const AtomicString& value = m_fontElement->getAttribute(vert_adv_yAttr);
    211        if (value.isEmpty())
    212         return 1.0f;
    213 
    214     return value.toFloat();
    215 }
    216 
    217 int SVGFontFaceElement::ascent() const
    218 {
    219     // Spec: Same syntax and semantics as the 'ascent' descriptor within an @font-face rule. The maximum
    220     // unaccented height of the font within the font coordinate system. If the attribute is not specified,
    221     // the effect is as if the attribute were set to the difference between the units-per-em value and the
    222     // vert-origin-y value for the corresponding font.
    223     const AtomicString& ascentValue = getAttribute(ascentAttr);
    224     if (!ascentValue.isEmpty())
    225         return static_cast<int>(ceilf(ascentValue.toFloat()));
    226 
    227     if (m_fontElement) {
    228         const AtomicString& vertOriginY = m_fontElement->getAttribute(vert_origin_yAttr);
    229         if (!vertOriginY.isEmpty())
    230             return static_cast<int>(unitsPerEm()) - static_cast<int>(ceilf(vertOriginY.toFloat()));
    231     }
    232 
    233     // Match Batiks default value
    234     return static_cast<int>(ceilf(unitsPerEm() * 0.8f));
    235 }
    236 
    237 int SVGFontFaceElement::descent() const
    238 {
    239     // Spec: Same syntax and semantics as the 'descent' descriptor within an @font-face rule. The maximum
    240     // unaccented depth of the font within the font coordinate system. If the attribute is not specified,
    241     // the effect is as if the attribute were set to the vert-origin-y value for the corresponding font.
    242     const AtomicString& descentValue = getAttribute(descentAttr);
    243     if (!descentValue.isEmpty()) {
    244         // 14 different W3C SVG 1.1 testcases use a negative descent value,
    245         // where a positive was meant to be used  Including:
    246         // animate-elem-24-t.svg, fonts-elem-01-t.svg, fonts-elem-02-t.svg (and 11 others)
    247         int descent = static_cast<int>(ceilf(descentValue.toFloat()));
    248         return descent < 0 ? -descent : descent;
    249     }
    250 
    251     if (m_fontElement) {
    252         const AtomicString& vertOriginY = m_fontElement->getAttribute(vert_origin_yAttr);
    253         if (!vertOriginY.isEmpty())
    254             return static_cast<int>(ceilf(vertOriginY.toFloat()));
    255     }
    256 
    257     // Match Batiks default value
    258     return static_cast<int>(ceilf(unitsPerEm() * 0.2f));
    259 }
    260 
    261 String SVGFontFaceElement::fontFamily() const
    262 {
    263     return m_styleDeclaration->getPropertyValue(CSSPropertyFontFamily);
    264 }
    265 
    266 void SVGFontFaceElement::rebuildFontFace()
    267 {
    268     ASSERT(inDocument());
    269 
    270     // we currently ignore all but the first src element, alternatively we could concat them
    271     SVGFontFaceSrcElement* srcElement = 0;
    272 
    273     for (Node* child = firstChild(); child && !srcElement; child = child->nextSibling()) {
    274         if (child->hasTagName(font_face_srcTag))
    275             srcElement = static_cast<SVGFontFaceSrcElement*>(child);
    276     }
    277 
    278     bool describesParentFont = parentNode()->hasTagName(SVGNames::fontTag);
    279     RefPtr<CSSValueList> list;
    280 
    281     if (describesParentFont) {
    282         m_fontElement = static_cast<SVGFontElement*>(parentNode());
    283 
    284         list = CSSValueList::createCommaSeparated();
    285         list->append(CSSFontFaceSrcValue::createLocal(fontFamily()));
    286     } else {
    287         m_fontElement = 0;
    288         if (srcElement)
    289             list = srcElement->srcValue();
    290     }
    291 
    292     if (!list)
    293         return;
    294 
    295     // Parse in-memory CSS rules
    296     CSSProperty srcProperty(CSSPropertySrc, list);
    297     const CSSProperty* srcPropertyRef = &srcProperty;
    298     m_styleDeclaration->addParsedProperties(&srcPropertyRef, 1);
    299 
    300     if (describesParentFont) {
    301         // Traverse parsed CSS values and associate CSSFontFaceSrcValue elements with ourselves.
    302         RefPtr<CSSValue> src = m_styleDeclaration->getPropertyCSSValue(CSSPropertySrc);
    303         CSSValueList* srcList = static_cast<CSSValueList*>(src.get());
    304 
    305         unsigned srcLength = srcList ? srcList->length() : 0;
    306         for (unsigned i = 0; i < srcLength; i++) {
    307             if (CSSFontFaceSrcValue* item = static_cast<CSSFontFaceSrcValue*>(srcList->itemWithoutBoundsCheck(i)))
    308                 item->setSVGFontFaceElement(this);
    309         }
    310     }
    311 
    312     document()->updateStyleSelector();
    313 }
    314 
    315 void SVGFontFaceElement::insertedIntoDocument()
    316 {
    317     SVGElement::insertedIntoDocument();
    318     document()->mappedElementSheet()->append(m_fontFaceRule);
    319     rebuildFontFace();
    320 }
    321 
    322 void SVGFontFaceElement::removedFromDocument()
    323 {
    324     removeFromMappedElementSheet();
    325     SVGElement::removedFromDocument();
    326 }
    327 
    328 void SVGFontFaceElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
    329 {
    330     SVGElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
    331     if (inDocument())
    332         rebuildFontFace();
    333 }
    334 
    335 void SVGFontFaceElement::removeFromMappedElementSheet()
    336 {
    337     CSSStyleSheet* mappedElementSheet = document()->mappedElementSheet();
    338     if (!mappedElementSheet)
    339         return;
    340 
    341     for (unsigned i = 0; i < mappedElementSheet->length(); ++i) {
    342         if (mappedElementSheet->item(i) == m_fontFaceRule) {
    343             mappedElementSheet->remove(i);
    344             break;
    345         }
    346     }
    347     document()->updateStyleSelector();
    348 }
    349 
    350 } // namespace WebCore
    351 
    352 #endif // ENABLE(SVG_FONTS)
    353