1/*
2 * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann@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 "SVGGlyph.h"
25
26namespace WebCore {
27
28// Helper functions to determine the arabic character forms (initial, medial, terminal, isolated)
29enum ArabicCharShapingMode {
30    SNone = 0,
31    SRight = 1,
32    SDual = 2
33};
34
35static const ArabicCharShapingMode s_arabicCharShapingMode[222] = {
36    SRight, SRight, SRight, SRight, SDual , SRight, SDual , SRight, SDual , SDual , SDual , SDual , SDual , SRight,                 /* 0x0622 - 0x062F */
37    SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SNone , SNone , SNone , SNone , SNone , /* 0x0630 - 0x063F */
38    SNone , SDual , SDual , SDual , SDual , SDual , SDual , SRight, SDual , SDual , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0640 - 0x064F */
39    SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0650 - 0x065F */
40    SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0660 - 0x066F */
41    SNone , SRight, SRight, SRight, SNone , SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x0670 - 0x067F */
42    SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, /* 0x0680 - 0x068F */
43    SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , /* 0x0690 - 0x069F */
44    SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x06A0 - 0x06AF */
45    SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x06B0 - 0x06BF */
46    SRight, SDual , SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SDual , SRight, SDual , SRight, /* 0x06C0 - 0x06CF */
47    SDual , SDual , SRight, SRight, SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x06D0 - 0x06DF */
48    SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x06E0 - 0x06EF */
49    SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SDual , SDual , SDual , SNone , SNone , SNone   /* 0x06F0 - 0x06FF */
50};
51
52static inline SVGGlyph::ArabicForm processArabicFormDetection(const UChar& curChar, bool& lastCharShapesRight, SVGGlyph::ArabicForm* prevForm)
53{
54    SVGGlyph::ArabicForm curForm;
55
56    ArabicCharShapingMode shapingMode = SNone;
57    if (curChar >= 0x0622 && curChar <= 0x06FF)
58        shapingMode = s_arabicCharShapingMode[curChar - 0x0622];
59
60    // Use a simple state machine to identify the actual arabic form
61    // It depends on the order of the arabic form enum:
62    // enum ArabicForm { None = 0, Isolated, Terminal, Initial, Medial };
63
64    if (lastCharShapesRight && shapingMode == SDual) {
65        if (prevForm) {
66            int correctedForm = (int) *prevForm + 1;
67            ASSERT(correctedForm >= SVGGlyph::None && correctedForm <= SVGGlyph::Medial);
68            *prevForm = static_cast<SVGGlyph::ArabicForm>(correctedForm);
69        }
70
71        curForm = SVGGlyph::Initial;
72    } else
73        curForm = shapingMode == SNone ? SVGGlyph::None : SVGGlyph::Isolated;
74
75    lastCharShapesRight = shapingMode != SNone;
76    return curForm;
77}
78
79Vector<SVGGlyph::ArabicForm> charactersWithArabicForm(const String& input, bool rtl)
80{
81    Vector<SVGGlyph::ArabicForm> forms;
82    unsigned length = input.length();
83
84    bool containsArabic = false;
85    for (unsigned i = 0; i < length; ++i) {
86        if (ublock_getCode(input[i]) == UBLOCK_ARABIC) {
87            containsArabic = true;
88            break;
89        }
90    }
91
92    if (!containsArabic)
93        return forms;
94
95    bool lastCharShapesRight = false;
96
97    // Start identifying arabic forms
98    if (rtl) {
99        for (int i = length - 1; i >= 0; --i)
100            forms.insert(0, processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.first()));
101    } else {
102        for (unsigned i = 0; i < length; ++i)
103            forms.append(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.last()));
104    }
105
106    return forms;
107}
108
109static inline bool isCompatibleArabicForm(const SVGGlyph& identifier, const Vector<SVGGlyph::ArabicForm>& chars, unsigned startPosition, unsigned endPosition)
110{
111    if (chars.isEmpty())
112        return true;
113
114    Vector<SVGGlyph::ArabicForm>::const_iterator realEnd = chars.end();
115    Vector<SVGGlyph::ArabicForm>::const_iterator it = chars.begin() + startPosition;
116    if (it >= realEnd)
117        return true;
118
119    Vector<SVGGlyph::ArabicForm>::const_iterator end = chars.begin() + endPosition;
120    if (end >= realEnd)
121        end = realEnd;
122
123    for (; it != end; ++it) {
124        if (*it != static_cast<SVGGlyph::ArabicForm>(identifier.arabicForm) && *it != SVGGlyph::None)
125            return false;
126    }
127
128    return true;
129}
130
131bool isCompatibleGlyph(const SVGGlyph& identifier, bool isVerticalText, const String& language,
132                       const Vector<SVGGlyph::ArabicForm>& chars, unsigned startPosition, unsigned endPosition)
133{
134    bool valid = true;
135
136    // Check wheter orientation if glyph fits within the request
137    switch (identifier.orientation) {
138    case SVGGlyph::Vertical:
139        valid = isVerticalText;
140        break;
141    case SVGGlyph::Horizontal:
142        valid = !isVerticalText;
143        break;
144    case SVGGlyph::Both:
145        break;
146    }
147
148    if (!valid)
149        return false;
150
151    // Check wheter languages are compatible
152    if (!identifier.languages.isEmpty()) {
153        // This glyph exists only in certain languages, if we're not specifying a
154        // language on the referencing element we're unable to use this glyph.
155        if (language.isEmpty())
156            return false;
157
158        // Split subcode from language, if existant.
159        String languagePrefix;
160
161        size_t subCodeSeparator = language.find('-');
162        if (subCodeSeparator != notFound)
163            languagePrefix = language.left(subCodeSeparator);
164
165        Vector<String>::const_iterator it = identifier.languages.begin();
166        Vector<String>::const_iterator end = identifier.languages.end();
167
168        bool found = false;
169        for (; it != end; ++it) {
170            const String& cur = *it;
171            if (cur == language || cur == languagePrefix) {
172                found = true;
173                break;
174            }
175        }
176
177        if (!found)
178            return false;
179    }
180
181    // Check wheter arabic form is compatible
182    return isCompatibleArabicForm(identifier, chars, startPosition, endPosition);
183}
184
185}
186
187#endif
188