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