• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 #include "platform/fonts/Character.h"
33 
34 #include "platform/fonts/FontPlatformFeatures.h"
35 #include "wtf/StdLibExtras.h"
36 #include "wtf/text/StringBuilder.h"
37 
38 using namespace WTF;
39 using namespace Unicode;
40 
41 namespace WebCore {
42 
43 const uint8_t Character::s_roundingHackCharacterTable[256] = {
44     0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*\t*/, 1 /*\n*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45     1 /*space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*-*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*?*/,
46     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
47     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
48     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
49     1 /*no-break space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
50     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
52 };
53 
54 static const UChar32 cjkIsolatedSymbolsArray[] = {
55     // 0x2C7 Caron, Mandarin Chinese 3rd Tone
56     0x2C7,
57     // 0x2CA Modifier Letter Acute Accent, Mandarin Chinese 2nd Tone
58     0x2CA,
59     // 0x2CB Modifier Letter Grave Access, Mandarin Chinese 4th Tone
60     0x2CB,
61     // 0x2D9 Dot Above, Mandarin Chinese 5th Tone
62     0x2D9,
63     0x2020, 0x2021, 0x2030, 0x203B, 0x203C, 0x2042, 0x2047, 0x2048, 0x2049, 0x2051,
64     0x20DD, 0x20DE, 0x2100, 0x2103, 0x2105, 0x2109, 0x210A, 0x2113, 0x2116, 0x2121,
65     0x212B, 0x213B, 0x2150, 0x2151, 0x2152, 0x217F, 0x2189, 0x2307, 0x2312, 0x23CE,
66     0x2423, 0x25A0, 0x25A1, 0x25A2, 0x25AA, 0x25AB, 0x25B1, 0x25B2, 0x25B3, 0x25B6,
67     0x25B7, 0x25BC, 0x25BD, 0x25C0, 0x25C1, 0x25C6, 0x25C7, 0x25C9, 0x25CB, 0x25CC,
68     0x25EF, 0x2605, 0x2606, 0x260E, 0x2616, 0x2617, 0x2640, 0x2642, 0x26A0, 0x26BD,
69     0x26BE, 0x2713, 0x271A, 0x273F, 0x2740, 0x2756, 0x2B1A, 0xFE10, 0xFE11, 0xFE12,
70     0xFE19, 0xFF1D,
71     // Emoji.
72     0x1F100
73 };
74 
75 // Takes a flattened list of closed intervals
76 template <class T, size_t size>
valueInIntervalList(const T (& intervalList)[size],const T & value)77 bool valueInIntervalList(const T (&intervalList)[size], const T& value)
78 {
79     const T* bound = std::upper_bound(&intervalList[0], &intervalList[size], value);
80     if ((bound - intervalList) % 2 == 1)
81         return true;
82     return bound > intervalList && *(bound - 1) == value;
83 }
84 
characterRangeCodePath(const UChar * characters,unsigned len)85 CodePath Character::characterRangeCodePath(const UChar* characters, unsigned len)
86 {
87     static const UChar complexCodePathRanges[] = {
88         // U+02E5 through U+02E9 (Modifier Letters : Tone letters)
89         0x2E5, 0x2E9,
90         // U+0300 through U+036F Combining diacritical marks
91         0x300, 0x36F,
92         // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, ...
93         0x0591, 0x05BD,
94         // ... Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha
95         0x05BF, 0x05CF,
96         // U+0600 through U+109F Arabic, Syriac, Thaana, NKo, Samaritan, Mandaic,
97         // Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada,
98         // Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
99         0x0600, 0x109F,
100         // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left
101         // here if you precompose; Modern Korean will be precomposed as a result of step A)
102         0x1100, 0x11FF,
103         // U+135D through U+135F Ethiopic combining marks
104         0x135D, 0x135F,
105         // U+1780 through U+18AF Tagalog, Hanunoo, Buhid, Taghanwa,Khmer, Mongolian
106         0x1700, 0x18AF,
107         // U+1900 through U+194F Limbu (Unicode 4.0)
108         0x1900, 0x194F,
109         // U+1980 through U+19DF New Tai Lue
110         0x1980, 0x19DF,
111         // U+1A00 through U+1CFF Buginese, Tai Tham, Balinese, Batak, Lepcha, Vedic
112         0x1A00, 0x1CFF,
113         // U+1DC0 through U+1DFF Comining diacritical mark supplement
114         0x1DC0, 0x1DFF,
115         // U+20D0 through U+20FF Combining marks for symbols
116         0x20D0, 0x20FF,
117         // U+2CEF through U+2CF1 Combining marks for Coptic
118         0x2CEF, 0x2CF1,
119         // U+302A through U+302F Ideographic and Hangul Tone marks
120         0x302A, 0x302F,
121         // U+A67C through U+A67D Combining marks for old Cyrillic
122         0xA67C, 0xA67D,
123         // U+A6F0 through U+A6F1 Combining mark for Bamum
124         0xA6F0, 0xA6F1,
125         // U+A800 through U+ABFF Nagri, Phags-pa, Saurashtra, Devanagari Extended,
126         // Hangul Jamo Ext. A, Javanese, Myanmar Extended A, Tai Viet, Meetei Mayek
127         0xA800, 0xABFF,
128         // U+D7B0 through U+D7FF Hangul Jamo Ext. B
129         0xD7B0, 0xD7FF,
130         // U+FE00 through U+FE0F Unicode variation selectors
131         0xFE00, 0xFE0F,
132         // U+FE20 through U+FE2F Combining half marks
133         0xFE20, 0xFE2F
134     };
135 
136     CodePath result = SimplePath;
137     for (unsigned i = 0; i < len; i++) {
138         const UChar c = characters[i];
139 
140         // Shortcut for common case
141         if (c < 0x2E5)
142             continue;
143 
144         // U+1E00 through U+2000 characters with diacritics and stacked diacritics
145         if (c >= 0x1E00 && c <= 0x2000) {
146             result = SimpleWithGlyphOverflowPath;
147             continue;
148         }
149 
150         // Surrogate pairs
151         if (c > 0xD7FF && c <= 0xDBFF) {
152             if (i == len - 1)
153                 continue;
154 
155             UChar next = characters[++i];
156             if (!U16_IS_TRAIL(next))
157                 continue;
158 
159             UChar32 supplementaryCharacter = U16_GET_SUPPLEMENTARY(c, next);
160 
161             if (supplementaryCharacter < 0x1F1E6) // U+1F1E6 through U+1F1FF Regional Indicator Symbols
162                 continue;
163             if (supplementaryCharacter <= 0x1F1FF)
164                 return ComplexPath;
165 
166             if (supplementaryCharacter < 0xE0100) // U+E0100 through U+E01EF Unicode variation selectors.
167                 continue;
168             if (supplementaryCharacter <= 0xE01EF)
169                 return ComplexPath;
170 
171             // FIXME: Check for Brahmi (U+11000 block), Kaithi (U+11080 block) and other complex scripts
172             // in plane 1 or higher.
173 
174             continue;
175         }
176 
177         // Search for other Complex cases
178         if (valueInIntervalList(complexCodePathRanges, c))
179             return ComplexPath;
180     }
181 
182     return result;
183 }
184 
isCJKIdeograph(UChar32 c)185 bool Character::isCJKIdeograph(UChar32 c)
186 {
187     static const UChar32 cjkIdeographRanges[] = {
188         // CJK Radicals Supplement and Kangxi Radicals.
189         0x2E80, 0x2FDF,
190         // CJK Strokes.
191         0x31C0, 0x31EF,
192         // CJK Unified Ideographs Extension A.
193         0x3400, 0x4DBF,
194         // The basic CJK Unified Ideographs block.
195         0x4E00, 0x9FFF,
196         // CJK Compatibility Ideographs.
197         0xF900, 0xFAFF,
198         // CJK Unified Ideographs Extension B.
199         0x20000, 0x2A6DF,
200         // CJK Unified Ideographs Extension C.
201         // CJK Unified Ideographs Extension D.
202         0x2A700, 0x2B81F,
203         // CJK Compatibility Ideographs Supplement.
204         0x2F800, 0x2FA1F
205     };
206     static size_t cjkIdeographRangesCount = WTF_ARRAY_LENGTH(cjkIdeographRanges);
207 
208     // Early out
209     if (c < cjkIdeographRanges[0] || c > cjkIdeographRanges[cjkIdeographRangesCount - 1])
210         return false;
211 
212     return valueInIntervalList(cjkIdeographRanges, c);
213 }
214 
isCJKIdeographOrSymbol(UChar32 c)215 bool Character::isCJKIdeographOrSymbol(UChar32 c)
216 {
217     // Likely common case
218     if (c < 0x2C7)
219         return false;
220 
221     // Hash lookup for isolated symbols (those not part of a contiguous range)
222     static HashSet<UChar32>* cjkIsolatedSymbols = 0;
223     if (!cjkIsolatedSymbols) {
224         cjkIsolatedSymbols = new HashSet<UChar32>();
225         for (size_t i = 0; i < WTF_ARRAY_LENGTH(cjkIsolatedSymbolsArray); ++i)
226             cjkIsolatedSymbols->add(cjkIsolatedSymbolsArray[i]);
227     }
228     if (cjkIsolatedSymbols->contains(c))
229         return true;
230 
231     if (isCJKIdeograph(c))
232         return true;
233 
234     static const UChar32 cjkSymbolRanges[] = {
235         0x2156, 0x215A,
236         0x2160, 0x216B,
237         0x2170, 0x217B,
238         0x23BE, 0x23CC,
239         0x2460, 0x2492,
240         0x249C, 0x24FF,
241         0x25CE, 0x25D3,
242         0x25E2, 0x25E6,
243         0x2600, 0x2603,
244         0x2660, 0x266F,
245         0x2672, 0x267D,
246         0x2776, 0x277F,
247         // Ideographic Description Characters, with CJK Symbols and Punctuation, excluding 0x3030.
248         // Then Hiragana 0x3040 .. 0x309F, Katakana 0x30A0 .. 0x30FF, Bopomofo 0x3100 .. 0x312F
249         0x2FF0, 0x302F,
250         0x3031, 0x312F,
251         // More Bopomofo and Bopomofo Extended 0x31A0 .. 0x31BF
252         0x3190, 0x31BF,
253         // Enclosed CJK Letters and Months (0x3200 .. 0x32FF).
254         // CJK Compatibility (0x3300 .. 0x33FF).
255         0x3200, 0x33FF,
256         0xF860, 0xF862,
257         // CJK Compatibility Forms.
258         0xFE30, 0xFE4F,
259         // Halfwidth and Fullwidth Forms
260         // Usually only used in CJK
261         0xFF00, 0xFF0C,
262         0xFF0E, 0xFF1A,
263         0xFF1F, 0xFFEF,
264         // Emoji.
265         0x1F110, 0x1F129,
266         0x1F130, 0x1F149,
267         0x1F150, 0x1F169,
268         0x1F170, 0x1F189,
269         0x1F200, 0x1F6FF
270     };
271 
272     return valueInIntervalList(cjkSymbolRanges, c);
273 }
274 
expansionOpportunityCount(const LChar * characters,size_t length,TextDirection direction,bool & isAfterExpansion)275 unsigned Character::expansionOpportunityCount(const LChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion)
276 {
277     unsigned count = 0;
278     if (direction == LTR) {
279         for (size_t i = 0; i < length; ++i) {
280             if (treatAsSpace(characters[i])) {
281                 count++;
282                 isAfterExpansion = true;
283             } else {
284                 isAfterExpansion = false;
285             }
286         }
287     } else {
288         for (size_t i = length; i > 0; --i) {
289             if (treatAsSpace(characters[i - 1])) {
290                 count++;
291                 isAfterExpansion = true;
292             } else {
293                 isAfterExpansion = false;
294             }
295         }
296     }
297     return count;
298 }
299 
expansionOpportunityCount(const UChar * characters,size_t length,TextDirection direction,bool & isAfterExpansion)300 unsigned Character::expansionOpportunityCount(const UChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion)
301 {
302     static bool expandAroundIdeographs = FontPlatformFeatures::canExpandAroundIdeographsInComplexText();
303     unsigned count = 0;
304     if (direction == LTR) {
305         for (size_t i = 0; i < length; ++i) {
306             UChar32 character = characters[i];
307             if (treatAsSpace(character)) {
308                 count++;
309                 isAfterExpansion = true;
310                 continue;
311             }
312             if (U16_IS_LEAD(character) && i + 1 < length && U16_IS_TRAIL(characters[i + 1])) {
313                 character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]);
314                 i++;
315             }
316             if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) {
317                 if (!isAfterExpansion)
318                     count++;
319                 count++;
320                 isAfterExpansion = true;
321                 continue;
322             }
323             isAfterExpansion = false;
324         }
325     } else {
326         for (size_t i = length; i > 0; --i) {
327             UChar32 character = characters[i - 1];
328             if (treatAsSpace(character)) {
329                 count++;
330                 isAfterExpansion = true;
331                 continue;
332             }
333             if (U16_IS_TRAIL(character) && i > 1 && U16_IS_LEAD(characters[i - 2])) {
334                 character = U16_GET_SUPPLEMENTARY(characters[i - 2], character);
335                 i--;
336             }
337             if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) {
338                 if (!isAfterExpansion)
339                     count++;
340                 count++;
341                 isAfterExpansion = true;
342                 continue;
343             }
344             isAfterExpansion = false;
345         }
346     }
347     return count;
348 }
349 
canReceiveTextEmphasis(UChar32 c)350 bool Character::canReceiveTextEmphasis(UChar32 c)
351 {
352     CharCategory category = Unicode::category(c);
353     if (category & (Separator_Space | Separator_Line | Separator_Paragraph | Other_NotAssigned | Other_Control | Other_Format))
354         return false;
355 
356     // Additional word-separator characters listed in CSS Text Level 3 Editor's Draft 3 November 2010.
357     if (c == ethiopicWordspace || c == aegeanWordSeparatorLine || c == aegeanWordSeparatorDot
358         || c == ugariticWordDivider || c == tibetanMarkIntersyllabicTsheg || c == tibetanMarkDelimiterTshegBstar)
359         return false;
360 
361     return true;
362 }
363 
364 template <typename CharacterType>
normalizeSpacesInternal(const CharacterType * characters,unsigned length)365 static inline String normalizeSpacesInternal(const CharacterType* characters, unsigned length)
366 {
367     StringBuilder normalized;
368     normalized.reserveCapacity(length);
369 
370     for (unsigned i = 0; i < length; ++i)
371         normalized.append(Character::normalizeSpaces(characters[i]));
372 
373     return normalized.toString();
374 }
375 
normalizeSpaces(const LChar * characters,unsigned length)376 String Character::normalizeSpaces(const LChar* characters, unsigned length)
377 {
378     return normalizeSpacesInternal(characters, length);
379 }
380 
normalizeSpaces(const UChar * characters,unsigned length)381 String Character::normalizeSpaces(const UChar* characters, unsigned length)
382 {
383     return normalizeSpacesInternal(characters, length);
384 }
385 
386 }
387