Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2011 Leo Yang <leoyang (at) webkit.org>
      3  *
      4  * This library is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU Library General Public
      6  * License as published by the Free Software Foundation; either
      7  * version 2 of the License, or (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * Library General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Library General Public License
     15  * along with this library; see the file COPYING.LIB.  If not, write to
     16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17  * Boston, MA 02110-1301, USA.
     18  */
     19 
     20 #include "config.h"
     21 
     22 #if ENABLE(SVG_FONTS)
     23 #include "core/svg/SVGAltGlyphDefElement.h"
     24 
     25 #include "SVGNames.h"
     26 #include "core/svg/SVGAltGlyphItemElement.h"
     27 #include "core/svg/SVGGlyphRefElement.h"
     28 
     29 namespace WebCore {
     30 
     31 inline SVGAltGlyphDefElement::SVGAltGlyphDefElement(const QualifiedName& tagName, Document* document)
     32     : SVGElement(tagName, document)
     33 {
     34     ASSERT(hasTagName(SVGNames::altGlyphDefTag));
     35     ScriptWrappable::init(this);
     36 }
     37 
     38 PassRefPtr<SVGAltGlyphDefElement> SVGAltGlyphDefElement::create(const QualifiedName& tagName, Document* document)
     39 {
     40     return adoptRef(new SVGAltGlyphDefElement(tagName, document));
     41 }
     42 
     43 bool SVGAltGlyphDefElement::hasValidGlyphElements(Vector<String>& glyphNames) const
     44 {
     45     // Spec: http://www.w3.org/TR/SVG/text.html#AltGlyphDefElement
     46     // An 'altGlyphDef' can contain either of the following:
     47     //
     48     // 1. In the simplest case, an 'altGlyphDef' contains one or more 'glyphRef' elements.
     49     // Each 'glyphRef' element references a single glyph within a particular font.
     50     // If all of the referenced glyphs are available, then these glyphs are rendered
     51     // instead of the character(s) inside of the referencing 'altGlyph' element.
     52     // If any of the referenced glyphs are unavailable, then the character(s) that are
     53     // inside of the 'altGlyph' element are rendered as if there were not an 'altGlyph'
     54     // element surrounding those characters.
     55     //
     56     // 2. In the more complex case, an 'altGlyphDef' contains one or more 'altGlyphItem' elements.
     57     // Each 'altGlyphItem' represents a candidate set of substitute glyphs. Each 'altGlyphItem'
     58     // contains one or more 'glyphRef' elements. Each 'glyphRef' element references a single
     59     // glyph within a particular font. The first 'altGlyphItem' in which all referenced glyphs
     60     // are available is chosen. The glyphs referenced from this 'altGlyphItem' are rendered
     61     // instead of the character(s) that are inside of the referencing 'altGlyph' element.
     62     // If none of the 'altGlyphItem' elements result in a successful match (i.e., none of the
     63     // 'altGlyphItem' elements has all of its referenced glyphs available), then the character(s)
     64     // that are inside of the 'altGlyph' element are rendered as if there were not an 'altGlyph'
     65     // element surrounding those characters.
     66     //
     67     // The spec doesn't tell how to deal with the mixing of <glyphRef> and <altGlyItem>.
     68     // However, we determine content model by the the type of the first appearing element
     69     // just like Opera 11 does. After the content model is determined we skip elements
     70     // which don't comform to it. For example:
     71     // a.    <altGlyphDef>
     72     //         <glyphRef id="g1" />
     73     //         <altGlyphItem id="i1"> ... </altGlyphItem>
     74     //         <glyphRef id="g2" />
     75     //       </altGlyphDef>
     76     //
     77     // b.    <altGlyphDef>
     78     //         <altGlyphItem id="i1"> ... </altGlyphItem>
     79     //         <altGlyphItem id="i2"> ... </altGlyphItem>
     80     //         <glyphRef id="g1" />
     81     //         <glyphRef id="g2" />
     82     //       </altGlyphDef>
     83     // For a), the content model is 1), so we will use "g1" and "g2" if they are all valid
     84     // and "i1" is skipped.
     85     // For b), the content model is 2), so we will use <glyphRef> elements contained in
     86     // "i1" if they are valid and "g1" and "g2" are skipped.
     87 
     88     // These 2 variables are used to determine content model.
     89     bool fountFirstGlyphRef = false;
     90     bool foundFirstAltGlyphItem = false;
     91 
     92     for (Node* child = firstChild(); child; child = child->nextSibling()) {
     93         if (!foundFirstAltGlyphItem && child->hasTagName(SVGNames::glyphRefTag)) {
     94             fountFirstGlyphRef = true;
     95             String referredGlyphName;
     96 
     97             if (static_cast<SVGGlyphRefElement*>(child)->hasValidGlyphElement(referredGlyphName))
     98                 glyphNames.append(referredGlyphName);
     99             else {
    100                 // As the spec says "If any of the referenced glyphs are unavailable,
    101                 // then the character(s) that are inside of the 'altGlyph' element are
    102                 // rendered as if there were not an 'altGlyph' element surrounding
    103                 // those characters.".
    104                 glyphNames.clear();
    105                 return false;
    106             }
    107         } else if (!fountFirstGlyphRef && child->hasTagName(SVGNames::altGlyphItemTag)) {
    108             foundFirstAltGlyphItem = true;
    109             Vector<String> referredGlyphNames;
    110 
    111             // As the spec says "The first 'altGlyphItem' in which all referenced glyphs
    112             // are available is chosen."
    113             if (static_cast<SVGAltGlyphItemElement*>(child)->hasValidGlyphElements(glyphNames) && !glyphNames.isEmpty())
    114                 return true;
    115         }
    116     }
    117     return !glyphNames.isEmpty();
    118 }
    119 
    120 }
    121 
    122 #endif
    123