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