2 * Copyright (C) 2014 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "platform/fonts/Character.h"
34 #include "platform/fonts/FontPlatformFeatures.h"
35 #include "wtf/StdLibExtras.h"
36 #include "wtf/text/StringBuilder.h"
39 using namespace Unicode;
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
54 static const UChar32 cjkIsolatedSymbolsArray[] = {
55 // 0x2C7 Caron, Mandarin Chinese 3rd Tone
57 // 0x2CA Modifier Letter Acute Accent, Mandarin Chinese 2nd Tone
59 // 0x2CB Modifier Letter Grave Access, Mandarin Chinese 4th Tone
61 // 0x2D9 Dot Above, Mandarin Chinese 5th Tone
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,
75 // Takes a flattened list of closed intervals
76 template <class T, size_t size>
77 bool valueInIntervalList(const T (&intervalList)[size], const T& value)
79 const T* bound = std::upper_bound(&intervalList[0], &intervalList[size], value);
80 if ((bound - intervalList) % 2 == 1)
82 return bound > intervalList && *(bound - 1) == value;
85 CodePath Character::characterRangeCodePath(const UChar* characters, unsigned len)
87 static const UChar complexCodePathRanges[] = {
88 // U+02E5 through U+02E9 (Modifier Letters : Tone letters)
90 // U+0300 through U+036F Combining diacritical marks
92 // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, ...
94 // ... Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha
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
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)
103 // U+135D through U+135F Ethiopic combining marks
105 // U+1780 through U+18AF Tagalog, Hanunoo, Buhid, Taghanwa,Khmer, Mongolian
107 // U+1900 through U+194F Limbu (Unicode 4.0)
109 // U+1980 through U+19DF New Tai Lue
111 // U+1A00 through U+1CFF Buginese, Tai Tham, Balinese, Batak, Lepcha, Vedic
113 // U+1DC0 through U+1DFF Comining diacritical mark supplement
115 // RIGHT-TO-LEFT MARK
117 // RIGHT-TO-LEFT OVERRIDE
119 // Nominal Digit Shape
121 // U+20D0 through U+20FF Combining marks for symbols
123 // U+2CEF through U+2CF1 Combining marks for Coptic
125 // U+302A through U+302F Ideographic and Hangul Tone marks
127 // U+A67C through U+A67D Combining marks for old Cyrillic
129 // U+A6F0 through U+A6F1 Combining mark for Bamum
131 // U+A800 through U+ABFF Nagri, Phags-pa, Saurashtra, Devanagari Extended,
132 // Hangul Jamo Ext. A, Javanese, Myanmar Extended A, Tai Viet, Meetei Mayek
134 // U+D7B0 through U+D7FF Hangul Jamo Ext. B
136 // U+FE00 through U+FE0F Unicode variation selectors
138 // U+FE20 through U+FE2F Combining half marks
142 CodePath result = SimplePath;
143 for (unsigned i = 0; i < len; i++) {
144 const UChar c = characters[i];
146 // Shortcut for common case
150 // U+1E00 through U+2000 characters with diacritics and stacked diacritics
151 if (c >= 0x1E00 && c <= 0x2000) {
152 result = SimpleWithGlyphOverflowPath;
157 if (c > 0xD7FF && c <= 0xDBFF) {
161 UChar next = characters[++i];
162 if (!U16_IS_TRAIL(next))
165 UChar32 supplementaryCharacter = U16_GET_SUPPLEMENTARY(c, next);
167 if (supplementaryCharacter < 0x1F1E6) // U+1F1E6 through U+1F1FF Regional Indicator Symbols
169 if (supplementaryCharacter <= 0x1F1FF)
172 if (supplementaryCharacter < 0xE0100) // U+E0100 through U+E01EF Unicode variation selectors.
174 if (supplementaryCharacter <= 0xE01EF)
177 // FIXME: Check for Brahmi (U+11000 block), Kaithi (U+11080 block) and other complex scripts
178 // in plane 1 or higher.
183 // Search for other Complex cases
184 if (valueInIntervalList(complexCodePathRanges, c))
191 bool Character::isCJKIdeograph(UChar32 c)
193 static const UChar32 cjkIdeographRanges[] = {
194 // CJK Radicals Supplement and Kangxi Radicals.
198 // CJK Unified Ideographs Extension A.
200 // The basic CJK Unified Ideographs block.
202 // CJK Compatibility Ideographs.
204 // CJK Unified Ideographs Extension B.
206 // CJK Unified Ideographs Extension C.
207 // CJK Unified Ideographs Extension D.
209 // CJK Compatibility Ideographs Supplement.
212 static size_t cjkIdeographRangesCount = WTF_ARRAY_LENGTH(cjkIdeographRanges);
215 if (c < cjkIdeographRanges[0] || c > cjkIdeographRanges[cjkIdeographRangesCount - 1])
218 return valueInIntervalList(cjkIdeographRanges, c);
221 bool Character::isCJKIdeographOrSymbol(UChar32 c)
223 // Likely common case
227 // Hash lookup for isolated symbols (those not part of a contiguous range)
228 static HashSet<UChar32>* cjkIsolatedSymbols = 0;
229 if (!cjkIsolatedSymbols) {
230 cjkIsolatedSymbols = new HashSet<UChar32>();
231 for (size_t i = 0; i < WTF_ARRAY_LENGTH(cjkIsolatedSymbolsArray); ++i)
232 cjkIsolatedSymbols->add(cjkIsolatedSymbolsArray[i]);
234 if (cjkIsolatedSymbols->contains(c))
237 if (isCJKIdeograph(c))
240 static const UChar32 cjkSymbolRanges[] = {
253 // Ideographic Description Characters, with CJK Symbols and Punctuation, excluding 0x3030.
254 // Then Hiragana 0x3040 .. 0x309F, Katakana 0x30A0 .. 0x30FF, Bopomofo 0x3100 .. 0x312F
257 // More Bopomofo and Bopomofo Extended 0x31A0 .. 0x31BF
259 // Enclosed CJK Letters and Months (0x3200 .. 0x32FF).
260 // CJK Compatibility (0x3300 .. 0x33FF).
263 // CJK Compatibility Forms.
265 // Halfwidth and Fullwidth Forms
266 // Usually only used in CJK
278 return valueInIntervalList(cjkSymbolRanges, c);
281 unsigned Character::expansionOpportunityCount(const LChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion)
284 if (direction == LTR) {
285 for (size_t i = 0; i < length; ++i) {
286 if (treatAsSpace(characters[i])) {
288 isAfterExpansion = true;
290 isAfterExpansion = false;
294 for (size_t i = length; i > 0; --i) {
295 if (treatAsSpace(characters[i - 1])) {
297 isAfterExpansion = true;
299 isAfterExpansion = false;
306 unsigned Character::expansionOpportunityCount(const UChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion)
308 static bool expandAroundIdeographs = FontPlatformFeatures::canExpandAroundIdeographsInComplexText();
310 if (direction == LTR) {
311 for (size_t i = 0; i < length; ++i) {
312 UChar32 character = characters[i];
313 if (treatAsSpace(character)) {
315 isAfterExpansion = true;
318 if (U16_IS_LEAD(character) && i + 1 < length && U16_IS_TRAIL(characters[i + 1])) {
319 character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]);
322 if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) {
323 if (!isAfterExpansion)
326 isAfterExpansion = true;
329 isAfterExpansion = false;
332 for (size_t i = length; i > 0; --i) {
333 UChar32 character = characters[i - 1];
334 if (treatAsSpace(character)) {
336 isAfterExpansion = true;
339 if (U16_IS_TRAIL(character) && i > 1 && U16_IS_LEAD(characters[i - 2])) {
340 character = U16_GET_SUPPLEMENTARY(characters[i - 2], character);
343 if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) {
344 if (!isAfterExpansion)
347 isAfterExpansion = true;
350 isAfterExpansion = false;
356 bool Character::canReceiveTextEmphasis(UChar32 c)
358 CharCategory category = Unicode::category(c);
359 if (category & (Separator_Space | Separator_Line | Separator_Paragraph | Other_NotAssigned | Other_Control | Other_Format))
362 // Additional word-separator characters listed in CSS Text Level 3 Editor's Draft 3 November 2010.
363 if (c == ethiopicWordspace || c == aegeanWordSeparatorLine || c == aegeanWordSeparatorDot
364 || c == ugariticWordDivider || c == tibetanMarkIntersyllabicTsheg || c == tibetanMarkDelimiterTshegBstar)
370 template <typename CharacterType>
371 static inline String normalizeSpacesInternal(const CharacterType* characters, unsigned length)
373 StringBuilder normalized;
374 normalized.reserveCapacity(length);
376 for (unsigned i = 0; i < length; ++i)
377 normalized.append(Character::normalizeSpaces(characters[i]));
379 return normalized.toString();
382 String Character::normalizeSpaces(const LChar* characters, unsigned length)
384 return normalizeSpacesInternal(characters, length);
387 String Character::normalizeSpaces(const UChar* characters, unsigned length)
389 return normalizeSpacesInternal(characters, length);