Home | History | Annotate | Download | only in fonts
      1 /*
      2  * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  */
     20 
     21 #include "config.h"
     22 
     23 #if ENABLE(SVG_FONTS)
     24 #include "platform/fonts/SVGGlyph.h"
     25 
     26 #include "wtf/unicode/Unicode.h"
     27 
     28 using namespace WTF::Unicode;
     29 
     30 namespace WebCore {
     31 
     32 // Helper functions to determine the arabic character forms (initial, medial, terminal, isolated)
     33 enum ArabicCharShapingMode {
     34     SNone = 0,
     35     SRight = 1,
     36     SDual = 2
     37 };
     38 
     39 static const ArabicCharShapingMode s_arabicCharShapingMode[222] = {
     40     SRight, SRight, SRight, SRight, SDual , SRight, SDual , SRight, SDual , SDual , SDual , SDual , SDual , SRight,                 /* 0x0622 - 0x062F */
     41     SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SNone , SNone , SNone , SNone , SNone , /* 0x0630 - 0x063F */
     42     SNone , SDual , SDual , SDual , SDual , SDual , SDual , SRight, SDual , SDual , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0640 - 0x064F */
     43     SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0650 - 0x065F */
     44     SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0660 - 0x066F */
     45     SNone , SRight, SRight, SRight, SNone , SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x0670 - 0x067F */
     46     SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, /* 0x0680 - 0x068F */
     47     SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , /* 0x0690 - 0x069F */
     48     SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x06A0 - 0x06AF */
     49     SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x06B0 - 0x06BF */
     50     SRight, SDual , SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SDual , SRight, SDual , SRight, /* 0x06C0 - 0x06CF */
     51     SDual , SDual , SRight, SRight, SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x06D0 - 0x06DF */
     52     SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x06E0 - 0x06EF */
     53     SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SDual , SDual , SDual , SNone , SNone , SNone   /* 0x06F0 - 0x06FF */
     54 };
     55 
     56 static inline SVGGlyph::ArabicForm processArabicFormDetection(const UChar& curChar, bool& lastCharShapesRight, SVGGlyph::ArabicForm* prevForm)
     57 {
     58     SVGGlyph::ArabicForm curForm;
     59 
     60     ArabicCharShapingMode shapingMode = SNone;
     61     if (curChar >= 0x0622 && curChar <= 0x06FF)
     62         shapingMode = s_arabicCharShapingMode[curChar - 0x0622];
     63 
     64     // Use a simple state machine to identify the actual arabic form
     65     // It depends on the order of the arabic form enum:
     66     // enum ArabicForm { None = 0, Isolated, Terminal, Initial, Medial };
     67 
     68     if (lastCharShapesRight && shapingMode == SDual) {
     69         if (prevForm) {
     70             int correctedForm = (int) *prevForm + 1;
     71             ASSERT(correctedForm >= SVGGlyph::None && correctedForm <= SVGGlyph::Medial);
     72             *prevForm = static_cast<SVGGlyph::ArabicForm>(correctedForm);
     73         }
     74 
     75         curForm = SVGGlyph::Initial;
     76     } else
     77         curForm = shapingMode == SNone ? SVGGlyph::None : SVGGlyph::Isolated;
     78 
     79     lastCharShapesRight = shapingMode != SNone;
     80     return curForm;
     81 }
     82 
     83 Vector<SVGGlyph::ArabicForm> charactersWithArabicForm(const String& input, bool rtl)
     84 {
     85     Vector<SVGGlyph::ArabicForm> forms;
     86     unsigned length = input.length();
     87 
     88     bool containsArabic = false;
     89     for (unsigned i = 0; i < length; ++i) {
     90         if (isArabicChar(input[i])) {
     91             containsArabic = true;
     92             break;
     93         }
     94     }
     95 
     96     if (!containsArabic)
     97         return forms;
     98 
     99     bool lastCharShapesRight = false;
    100 
    101     // Start identifying arabic forms
    102     if (rtl) {
    103         for (int i = length - 1; i >= 0; --i)
    104             forms.prepend(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.first()));
    105     } else {
    106         for (unsigned i = 0; i < length; ++i)
    107             forms.append(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.last()));
    108     }
    109 
    110     return forms;
    111 }
    112 
    113 static inline bool isCompatibleArabicForm(const SVGGlyph& identifier, const Vector<SVGGlyph::ArabicForm>& chars, unsigned startPosition, unsigned endPosition)
    114 {
    115     if (chars.isEmpty())
    116         return true;
    117 
    118     Vector<SVGGlyph::ArabicForm>::const_iterator realEnd = chars.end();
    119     Vector<SVGGlyph::ArabicForm>::const_iterator it = chars.begin() + startPosition;
    120     if (it >= realEnd)
    121         return true;
    122 
    123     Vector<SVGGlyph::ArabicForm>::const_iterator end = chars.begin() + endPosition;
    124     if (end >= realEnd)
    125         end = realEnd;
    126 
    127     for (; it != end; ++it) {
    128         if (*it != static_cast<SVGGlyph::ArabicForm>(identifier.arabicForm) && *it != SVGGlyph::None)
    129             return false;
    130     }
    131 
    132     return true;
    133 }
    134 
    135 bool isCompatibleGlyph(const SVGGlyph& identifier, bool isVerticalText, const String& language,
    136                        const Vector<SVGGlyph::ArabicForm>& chars, unsigned startPosition, unsigned endPosition)
    137 {
    138     bool valid = true;
    139 
    140     // Check wheter orientation if glyph fits within the request
    141     switch (identifier.orientation) {
    142     case SVGGlyph::Vertical:
    143         valid = isVerticalText;
    144         break;
    145     case SVGGlyph::Horizontal:
    146         valid = !isVerticalText;
    147         break;
    148     case SVGGlyph::Both:
    149         break;
    150     }
    151 
    152     if (!valid)
    153         return false;
    154 
    155     // Check wheter languages are compatible
    156     if (!identifier.languages.isEmpty()) {
    157         // This glyph exists only in certain languages, if we're not specifying a
    158         // language on the referencing element we're unable to use this glyph.
    159         if (language.isEmpty())
    160             return false;
    161 
    162         // Split subcode from language, if existant.
    163         String languagePrefix;
    164 
    165         size_t subCodeSeparator = language.find('-');
    166         if (subCodeSeparator != kNotFound)
    167             languagePrefix = language.left(subCodeSeparator);
    168 
    169         Vector<String>::const_iterator it = identifier.languages.begin();
    170         Vector<String>::const_iterator end = identifier.languages.end();
    171 
    172         bool found = false;
    173         for (; it != end; ++it) {
    174             const String& cur = *it;
    175             if (cur == language || cur == languagePrefix) {
    176                 found = true;
    177                 break;
    178             }
    179         }
    180 
    181         if (!found)
    182             return false;
    183     }
    184 
    185     // Check wheter arabic form is compatible
    186     return isCompatibleArabicForm(identifier, chars, startPosition, endPosition);
    187 }
    188 
    189 }
    190 
    191 #endif
    192