3bf0fcaf9e43e9d0bcced8495db54e050d9ec75e
[WebKit-https.git] / WebKit / Misc.subproj / WebUnicode.m
1 /*      
2         WebUnicode.m
3         Copyright 2001, 2002, Apple Computer, Inc.
4 */
5 #import <WebKit/WebUnicode.h>
6
7 #import <WebCore/WebCoreUnicode.h>
8
9 #define CELL(ucs) ((unsigned char) ucs & 0xff)
10 #define ROW(ucs) ((unsigned char) (ucs>>8)&0xff)
11
12
13 static int _unicodeDigitValue(UniChar c)
14 {
15     const char *dec_row = decimal_info[ROW(c)];
16     if( !dec_row )
17         return -1;
18     return dec_row[CELL(c)];
19 }
20
21 static WebCoreUnicodeCategory _unicodeCategory(UniChar c)
22 {
23     return (WebCoreUnicodeCategory)(unicode_info[ROW(c)][CELL(c)]);
24 }
25
26 static WebCoreUnicodeDirection _unicodeDirection(UniChar c)
27 {
28     const unsigned char *rowp = direction_info[ROW(c)];
29     
30     if(!rowp) 
31         return DirectionL;
32     return (WebCoreUnicodeDirection) ( *(rowp+CELL(c)) &0x1f );
33 }
34
35 static WebCoreUnicodeJoining _unicodeJoining(UniChar c)
36 {
37     const unsigned char *rowp = direction_info[ROW(c)];
38     if ( !rowp )
39         return JoiningOther;
40     return (WebCoreUnicodeJoining) ((*(rowp+CELL(c)) >> 5) &0x3);
41 }
42
43 static WebCoreUnicodeDecomposition _unicodeDecompositionTag(UniChar c)
44 {
45     const unsigned short *r = decomposition_info[ROW(c)];
46     if(!r)
47         return DecompositionSingle;
48
49     unsigned short pos = r[CELL(c)];
50     if(!pos)
51         return DecompositionSingle;
52
53     return (WebCoreUnicodeDecomposition) decomposition_map[pos];
54 }
55
56 static bool _unicodeMirrored(UniChar c)
57 {
58     const unsigned char *rowp = direction_info[ROW(c)];
59     if ( !rowp )
60         return FALSE;
61     return *(rowp+CELL(c))>128;
62 }
63
64 static UniChar _unicodeMirroredChar(UniChar c)
65 {
66     if(!_unicodeMirrored(c))
67         return c;
68
69     int i;
70     for (i = 0; i < symmetricPairsSize; i ++) {
71         if (symmetricPairs[i] == c)
72           return symmetricPairs[(i%2) ? (i-1) : (i+1)];
73     }
74     return c;
75 }
76
77 static WebCoreUnicodeCombiningClass _unicodeCombiningClass (UniChar c)
78 {
79     const unsigned char *rowp = combining_info[ROW(c)];
80     if ( !rowp )
81         return 0;
82     return *(rowp+CELL(c));
83 }
84
85 static UniChar _unicodeLower(UniChar c)
86 {
87     if ( _unicodeCategory(c) != Letter_Uppercase )
88         return c;
89     unsigned short lower = *( case_info[ROW(c)] + CELL(c) );
90     if ( lower == 0 )
91         return c;
92     return lower;
93 }
94
95 static UniChar _unicodeUpper(UniChar c)
96 {
97     if ( _unicodeCategory(c) != Letter_Lowercase )
98         return c;
99     unsigned short upper = *(case_info[ROW(c)]+CELL(c));
100     if ( upper == 0 )
101         return c;
102     return upper;
103 }
104
105 void WebKitInitializeUnicode(void)
106 {
107     WebCoreUnicodeDigitValueFunction = _unicodeDigitValue;
108     WebCoreUnicodeCategoryFunction = _unicodeCategory;
109     WebCoreUnicodeDirectionFunction = _unicodeDirection;
110     WebCoreUnicodeJoiningFunction = _unicodeJoining;
111     WebCoreUnicodeDecompositionTagFunction = _unicodeDecompositionTag;
112     WebCoreUnicodeMirroredFunction = _unicodeMirrored;
113     WebCoreUnicodeMirroredCharFunction = _unicodeMirroredChar;
114     WebCoreUnicodeCombiningClassFunction = _unicodeCombiningClass;
115     WebCoreUnicodeLowerFunction = _unicodeLower;
116     WebCoreUnicodeUpperFunction = _unicodeUpper;
117 }
118
119 #ifdef QT_CODE
120
121 // small class used internally in QString::Compose()
122 class QLigature
123 {
124 public:
125     QLigature( QChar c );
126
127     Q_UINT16 first() { cur = ligatures; return cur ? *cur : 0; }
128     Q_UINT16 next() { return cur && *cur ? *(cur++) : 0; }
129     Q_UINT16 current() { return cur ? *cur : 0; }
130
131     int match(QString & str, unsigned int index);
132     QChar head();
133     QChar::Decomposition tag();
134
135 private:
136     Q_UINT16 *ligatures;
137     Q_UINT16 *cur;
138 };
139
140 QLigature::QLigature( QChar c )
141 {
142     const Q_UINT16 *r = ligature_info[c.row()];
143     if( !r )
144         ligatures = 0;
145     else
146     {
147         const Q_UINT16 pos = r[c.cell()];
148         ligatures = (Q_UINT16 *)&(ligature_map[pos]);
149     }
150     cur = ligatures;
151 }
152
153 QChar QLigature::head()
154 {
155     if(current())
156         return QChar(decomposition_map[current()+1]);
157
158     return QChar::null;
159 }
160
161 QChar::Decomposition QLigature::tag()
162 {
163     if(current())
164         return (QChar::Decomposition) decomposition_map[current()];
165
166     return QChar::Canonical;
167 }
168
169 int QLigature::match(QString & str, unsigned int index)
170 {
171     unsigned int i=index;
172
173     if(!current()) return 0;
174
175     Q_UINT16 lig = current() + 2;
176     Q_UINT16 ch;
177
178     while ((i < str.length()) && (ch = decomposition_map[lig])) {
179         if (str[(int)i] != QChar(ch))
180             return 0;
181         i++; lig++;
182     }
183
184     if (!decomposition_map[lig])
185     {
186         return i-index;
187     }
188     return 0;
189 }
190
191
192 // this function is just used in QString::compose()
193 static inline bool format(QChar::Decomposition tag, QString & str,
194                           int index, int len)
195 {
196     unsigned int l = index + len;
197     unsigned int r = index;
198
199     bool left = FALSE, right = FALSE;
200
201     left = ((l < str.length()) &&
202             ((str[(int)l].joining() == QChar::Dual) ||
203              (str[(int)l].joining() == QChar::Right)));
204     if (r > 0) {
205         r--;
206         //printf("joining(right) = %d\n", str[(int)r].joining());
207         right = (str[(int)r].joining() == QChar::Dual);
208     }
209
210
211     switch (tag) {
212     case QChar::Medial:
213         return (left & right);
214     case QChar::Initial:
215         return (left && !right);
216     case QChar::Final:
217         return (right);// && !left);
218     case QChar::Isolated:
219     default:
220         return (!right && !left);
221     }
222 } // format()
223
224 /*
225   QString::compose() and visual() were developed by Gordon Tisher
226   <tisher@uniserve.ca>, with input from Lars Knoll <knoll@mpi-hd.mpg.de>,
227   who developed the unicode data tables.
228 */
229 /*!
230     \warning This function is not supported in Qt 3.x. It is provided
231     for experimental and illustrative purposes only. It is mainly of
232     interest to those experimenting with Arabic and other
233     composition-rich texts.
234
235     Applies possible ligatures to a QString. Useful when
236     composition-rich text requires rendering with glyph-poor fonts,
237     but it also makes compositions such as QChar(0x0041) ('A') and
238     QChar(0x0308) (Unicode accent diaresis), giving QChar(0x00c4)
239     (German A Umlaut).
240 */
241 void QString::compose()
242 {
243 #ifndef QT_NO_UNICODETABLES
244     unsigned int index=0, len;
245     unsigned int cindex = 0;
246
247     QChar code, head;
248
249     QMemArray<QChar> dia;
250
251     QString composed = *this;
252
253     while (index < length()) {
254         code = at(index);
255         //printf("\n\nligature for 0x%x:\n", code.unicode());
256         QLigature ligature(code);
257         ligature.first();
258         while(ligature.current()) {
259             if ((len = ligature.match(*this, index)) != 0) {
260                 head = ligature.head();
261                 unsigned short code = head.unicode();
262                 // we exclude Arabic presentation forms A and a few
263                 // other ligatures, which are undefined in most fonts
264                 if(!(code > 0xfb50 && code < 0xfe80) &&
265                    !(code > 0xfb00 && code < 0xfb2a)) {
266                                 // joining info is only needed for Arabic
267                     if (format(ligature.tag(), *this, index, len)) {
268                         //printf("using ligature 0x%x, len=%d\n",code,len);
269                         // replace letter
270                         composed.replace(cindex, len, QChar(head));
271                         index += len-1;
272                         // we continue searching in case we have a final
273                         // form because medial ones are preferred.
274                         if ( len != 1 || ligature.tag() !=QChar::Final )
275                             break;
276                     }
277                 }
278             }
279             ligature.next();
280         }
281         cindex++;
282         index++;
283     }
284     *this = composed;
285 #endif
286 }
287
288
289
290
291
292
293 bool QChar::isPrint() const
294 {
295     Category c = category();
296     return !(c == Other_Control || c == Other_NotAssigned);
297 }
298
299 /*!
300     Returns TRUE if the character is a separator character
301     (Separator_* categories); otherwise returns FALSE.
302 */
303 bool QChar::isSpace() const
304 {
305     if( !row() )
306         if( cell() >= 9 && cell() <=13 ) return TRUE;
307     Category c = category();
308     return c >= Separator_Space && c <= Separator_Paragraph;
309 }
310
311 /*!
312     Returns TRUE if the character is a mark (Mark_* categories);
313     otherwise returns FALSE.
314 */
315 bool QChar::isMark() const
316 {
317     Category c = category();
318     return c >= Mark_NonSpacing && c <= Mark_Enclosing;
319 }
320
321 /*!
322     Returns TRUE if the character is a punctuation mark (Punctuation_*
323     categories); otherwise returns FALSE.
324 */
325 bool QChar::isPunct() const
326 {
327     Category c = category();
328     return (c >= Punctuation_Connector && c <= Punctuation_Other);
329 }
330
331 /*!
332     Returns TRUE if the character is a letter (Letter_* categories);
333     otherwise returns FALSE.
334 */
335 bool QChar::isLetter() const
336 {
337     Category c = category();
338     return (c >= Letter_Uppercase && c <= Letter_Other);
339 }
340
341 /*!
342     Returns TRUE if the character is a number (of any sort - Number_*
343     categories); otherwise returns FALSE.
344
345     \sa isDigit()
346 */
347 bool QChar::isNumber() const
348 {
349     Category c = category();
350     return c >= Number_DecimalDigit && c <= Number_Other;
351 }
352
353 /*!
354     Returns TRUE if the character is a letter or number (Letter_* or
355     Number_* categories); otherwise returns FALSE.
356 */
357 bool QChar::isLetterOrNumber() const
358 {
359     Category c = category();
360     return (c >= Letter_Uppercase && c <= Letter_Other)
361         || (c >= Number_DecimalDigit && c <= Number_Other);
362 }
363
364
365 /*!
366     Returns TRUE if the character is a decimal digit
367     (Number_DecimalDigit); otherwise returns FALSE.
368 */
369 bool QChar::isDigit() const
370 {
371     return (category() == Number_DecimalDigit);
372 }
373
374
375 /*!
376     Returns TRUE if the character is a symbol (Symbol_* categories);
377     otherwise returns FALSE.
378 */
379 bool QChar::isSymbol() const
380 {
381     Category c = category();
382     return c >= Symbol_Math && c <= Symbol_Other;
383 }
384 #endif