From 817f4063a120d148d74e3fd8f63d95a4fba5b8d2 Mon Sep 17 00:00:00 2001 From: Vincent Wei Date: Wed, 16 Jan 2019 20:34:51 +0800 Subject: [PATCH] Add UNICODE utilities: IsGlyphAlnum, GlyphToUpper, ... --- include/gdi.h | 121 +++++++----- src/font/charset.c | 388 +++++++++++++++++++++++++++++--------- src/font/gunichartables.h | 9 +- 3 files changed, 375 insertions(+), 143 deletions(-) diff --git a/include/gdi.h b/include/gdi.h index eb82a31f..d32b0a7b 100644 --- a/include/gdi.h +++ b/include/gdi.h @@ -6752,6 +6752,8 @@ MG_EXPORT int GUIAPI GetFirstMCharLen (PLOGFONT log_font, MG_EXPORT int GUIAPI GetFirstWord (PLOGFONT log_font, const char* mstr, int len, WORDINFO* word_info); +typedef int Glyph32; + #ifdef _MGCHARSET_UNICODE #include @@ -6909,6 +6911,80 @@ MG_EXPORT int GUIAPI WCS2MBSEx (PLOGFONT log_font, unsigned char* dest, WCS2MBSEx (log_font, dest, wcs, wcs_len, sizeof (wchar_t) == 4, \ n, NULL) +/** The function determines whether a glyph is alphanumeric. */ +MG_EXPORT BOOL GUIAPI IsGlyphAlnum(Glyph32 g); + +/** The function determines whether a character is alphabetic (i.e. a letter). */ +MG_EXPORT BOOL GUIAPI IsGlyphAlpha(Glyph32 g); + +/** The function determines whether a character is a control character. */ +MG_EXPORT BOOL GUIAPI IsGlyphControl(Glyph32 g); + +/** The function determines whether a character is numeric (i.e. a digit). */ +MG_EXPORT BOOL GUIAPI IsGlyphDigit(Glyph32 g); + +/** The function determines whether a character is printable and not a space */ +MG_EXPORT BOOL GUIAPI IsGlyphGraph(Glyph32 g); + +/** The function determines whether a character is a lowercase letter. */ +MG_EXPORT BOOL GUIAPI IsGlyphLowercase(Glyph32 g); + +/** The function determines whether a character is printable. */ +MG_EXPORT BOOL GUIAPI IsGlyphPrint(Glyph32 g); + +/** The function determines whether a character is a uppercase letter. */ +MG_EXPORT BOOL GUIAPI IsGlyphUppercase(Glyph32 g); + +/** The function determines whether a character is punctuation or a symbol. */ +MG_EXPORT BOOL GUIAPI IsGlyphPunct(Glyph32 g); + +/** The function determines whether a character is a space, tab, or line separator + * (newline, carriage return, etc.). */ +MG_EXPORT BOOL GUIAPI IsGlyphSpace(Glyph32 g); + +/** The function determines whether a character is a mark (non-spacing mark, + * combining mark, or enclosing mark in Unicode speak). */ +MG_EXPORT BOOL GUIAPI IsGlyphMark(Glyph32 g); + +/** The function determines if a character is titlecase. Some characters in + * Unicode which are composites, such as the DZ digraph + * have three case variants instead of just two. The titlecase + * form is used at the beginning of a word where only the + * first letter is capitalized. The titlecase form of the DZ + * digraph is U+01F2 LATIN CAPITAL LETTTER D WITH SMALL LETTER Z. */ +MG_EXPORT BOOL GUIAPI IsGlyphTitle(Glyph32 g); + +/** The function determines if a character is a hexidecimal digit. */ +MG_EXPORT BOOL GUIAPI IsGlyphXDigit(Glyph32 g); + +/** The function determines if a given character is assigned in the Unicode standard. */ +MG_EXPORT BOOL GUIAPI IsGlyphDefined(Glyph32 g); + +/** The function determines if a given character typically takes zero width when rendered. */ +MG_EXPORT BOOL GUIAPI IsGlyphZeroWidth(Glyph32 g); + +/** The function determines if a character is typically rendered in a double-width cell. */ +MG_EXPORT BOOL GUIAPI IsGlyphWide(Glyph32 g); + +/** + * The function determines if a character is typically rendered in a double-width + * cell under legacy East Asian locales. If a character is wide according to + * g_unichar_iswide(), then it is also reported wide with this function, but + * the converse is not necessarily true. See the + * [Unicode Standard Annex #11](http://www.unicode.org/reports/tr11/) + * for details. + */ +MG_EXPORT BOOL GUIAPI IsGlyphWideCJK(Glyph32 g); + +/** Converts a character to uppercase. */ +MG_EXPORT Glyph32 GlyphToUpper (Glyph32 g); + +/** Converts a character to lower case. */ +MG_EXPORT Glyph32 GUIAPI GlyphToLower (Glyph32 g); + +/** Converts a glyph to the titlecase. */ +MG_EXPORT Glyph32 GUIAPI GlyphToTitle (Glyph32 g); + #endif /* _MGCHARSET_UNICODE */ /** @@ -8793,7 +8869,6 @@ MG_EXPORT void GUIAPI DestroyBMPFont (DEVFONT* dev_font); * * @{ */ -typedef int Glyph32; /** * \def INV_GLYPH_VALUE */ @@ -9010,49 +9085,9 @@ MG_EXPORT int GUIAPI DrawGlyphString (HDC hdc, int x, int y, Glyph32* glyphs, #define GLYPHBMP_TYPE_PRERENDER 0x03 /** The bits mask for glyph type */ -#define GLYPHTYPE_BASIC_MASK 0x000000FF +#define GLYPHTYPE_BASIC_MASK 0x00FF /** The bits mask for break type */ -#define GLYPHTYPE_BREAK_MASK 0x0000FF00 - -/** The bit determines whether a glyph is alphanumeric. */ -#define GLYPHTYPE_ATTR_ISALNUM 0x00010000 -/** The bit determines whether a character is alphabetic (i.e. a letter). */ -#define GLYPHTYPE_ATTR_ISALPHA 0x00020000 -/** The bit determines whether a character is a control character. */ -#define GLYPHTYPE_ATTR_ISCONTROL 0x00040000 -/** The bit determines whether a character is numeric (i.e. a digit). */ -#define GLYPHTYPE_ATTR_ISDIGIT 0x00080000 -/** The bit determines whether a character is printable and not a space */ -#define GLYPHTYPE_ATTR_ISGRAPH 0x00100000 -/** The bit determines whether a character is a lowercase letter. */ -#define GLYPHTYPE_ATTR_ISLOWER 0x00200000 -/** The bit determines whether a character is printable. */ -#define GLYPHTYPE_ATTR_ISPRINT 0x00400000 -/** The bit determines whether a character is a uppercase letter. */ -#define GLYPHTYPE_ATTR_ISUPPER 0x00800000 -/** The bit determines whether a character is punctuation or a symbol. */ -#define GLYPHTYPE_ATTR_ISPUNCT 0x01000000 -/** The bit determines whether a character is a space, tab, or line separator - * (newline, carriage return, etc.). */ -#define GLYPHTYPE_ATTR_ISSPACE 0x02000000 -/** The bit determines whether a character is a mark (non-spacing mark, - * combining mark, or enclosing mark in Unicode speak). */ -#define GLYPHTYPE_ATTR_ISMARK 0x04000000 -/** The bit determines if a character is titlecase. Some characters in - * Unicode which are composites, such as the DZ digraph - * have three case variants instead of just two. The titlecase - * form is used at the beginning of a word where only the - * first letter is capitalized. The titlecase form of the DZ - * digraph is U+01F2 LATIN CAPITAL LETTTER D WITH SMALL LETTER Z. */ -#define GLYPHTYPE_ATTR_ISTITLE 0x08000000 -/** The bit determines if a character is a hexidecimal digit. */ -#define GLYPHTYPE_ATTR_ISXDIGIT 0x10000000 -/** The bit determines if a given character is assigned in the Unicode standard. */ -#define GLYPHTYPE_ATTR_ISDEFINED 0x20000000 -/** The bit determines if a given character typically takes zero width when rendered. */ -#define GLYPHTYPE_ATTR_ISZEROWIDTH 0x40000000 -/** The bit determines if a character is typically rendered in a double-width cell. */ -#define GLYPHTYPE_ATTR_ISWIDE 0x80000000 +#define GLYPHTYPE_BREAK_MASK 0xFF00 /** * \var typedef struct _GLYPHINFO GLYPHINFO diff --git a/src/font/charset.c b/src/font/charset.c index f106009c..6cac31a1 100644 --- a/src/font/charset.c +++ b/src/font/charset.c @@ -3442,7 +3442,7 @@ static int utf8_len_first_char (const unsigned char* mstr, int len) return 1; } -static Glyph32 utf8_char_glyph_value (const unsigned char* pre_mchar, int pre_len, +static Glyph32 utf8_char_glyph_value (const unsigned char* pre_mchar, int pre_len, const unsigned char* cur_mchar, int cur_len) { UChar32 wc = *((unsigned char *)(cur_mchar++)); @@ -3543,99 +3543,7 @@ static unsigned int unicode_glyph_type (Glyph32 glyph_value) { unsigned int basic_type = TYPE(glyph_value); unsigned int break_type = PROP(glyph_value); - unsigned int type = basic_type | (break_type << 8); - unsigned int i; - - if (ISALDIGIT (basic_type)) - type |= GLYPHTYPE_ATTR_ISALNUM; - if (ISALPHA (basic_type)) - type |= GLYPHTYPE_ATTR_ISALPHA; - if (basic_type == G_UNICODE_CONTROL) - type |= GLYPHTYPE_ATTR_ISCONTROL; - if (basic_type == G_UNICODE_DECIMAL_NUMBER); - type |= GLYPHTYPE_ATTR_ISDIGIT; - if (!IS (basic_type, - OR (G_UNICODE_CONTROL, - OR (G_UNICODE_FORMAT, - OR (G_UNICODE_UNASSIGNED, - OR (G_UNICODE_SURROGATE, - OR (G_UNICODE_SPACE_SEPARATOR, - 0))))))) - type |= GLYPHTYPE_ATTR_ISGRAPH; - if (basic_type == G_UNICODE_LOWERCASE_LETTER) - type |= GLYPHTYPE_ATTR_ISLOWER; - if (basic_type == G_UNICODE_UPPERCASE_LETTER) - type |= GLYPHTYPE_ATTR_ISUPPER; - if (!IS (basic_type, - OR (G_UNICODE_CONTROL, - OR (G_UNICODE_FORMAT, - OR (G_UNICODE_UNASSIGNED, - OR (G_UNICODE_SURROGATE, - 0)))))) - type |= GLYPHTYPE_ATTR_ISPRINT; - if (IS (basic_type, - OR (G_UNICODE_CONNECT_PUNCTUATION, - OR (G_UNICODE_DASH_PUNCTUATION, - OR (G_UNICODE_CLOSE_PUNCTUATION, - OR (G_UNICODE_FINAL_PUNCTUATION, - OR (G_UNICODE_INITIAL_PUNCTUATION, - OR (G_UNICODE_OTHER_PUNCTUATION, - OR (G_UNICODE_OPEN_PUNCTUATION, - OR (G_UNICODE_CURRENCY_SYMBOL, - OR (G_UNICODE_MODIFIER_SYMBOL, - OR (G_UNICODE_MATH_SYMBOL, - OR (G_UNICODE_OTHER_SYMBOL, - 0))))))))))))) - type |= GLYPHTYPE_ATTR_ISPUNCT; - - switch (glyph_value) { - /* special-case these since Unicode thinks they are not spaces */ - case '\t': - case '\n': - case '\r': - case '\f': - type |= GLYPHTYPE_ATTR_ISSPACE; - break; - - default: { - if (IS (basic_type, - OR (G_UNICODE_SPACE_SEPARATOR, - OR (G_UNICODE_LINE_SEPARATOR, - OR (G_UNICODE_PARAGRAPH_SEPARATOR, - 0))))) - type |= GLYPHTYPE_ATTR_ISSPACE; - } - break; - } - - for (i = 0; i < TABLESIZE (title_table); ++i) { - if (title_table[i][0] == glyph_value) { - type |= GLYPHTYPE_ATTR_ISTITLE; - break; - } - } - - if (ISMARK (basic_type)) - type |= GLYPHTYPE_ATTR_ISMARK; - - if ((glyph_value >= 'a' && glyph_value <= 'f') - || (glyph_value >= 'A' && glyph_value <= 'F') - || (basic_type == G_UNICODE_DECIMAL_NUMBER)) - type |= GLYPHTYPE_ATTR_ISXDIGIT; - - if (!IS (basic_type, - OR (G_UNICODE_UNASSIGNED, - OR (G_UNICODE_SURROGATE, - 0)))) - type |= GLYPHTYPE_ATTR_ISDEFINED; - - if (glyph_value != 0x00AD && ISZEROWIDTHTYPE (basic_type)) - type |= GLYPHTYPE_ATTR_ISZEROWIDTH; - else if ((glyph_value >= 0x1160 && glyph_value < 0x1200) - || glyph_value == 0x200B) - type |= GLYPHTYPE_ATTR_ISZEROWIDTH; - - return type; + return basic_type | (break_type << 8); } #include "unicode-bidi-tables.h" @@ -4459,3 +4367,295 @@ BOOL IsCompatibleCharset (const char* charset, CHARSETOPS* ops) return FALSE; } +#ifdef _MGCHARSET_UNICODE + +BOOL GUIAPI IsGlyphAlnum(Glyph32 g) +{ + return ISALDIGIT(TYPE(REAL_GLYPH(g))); +} + +BOOL GUIAPI IsGlyphAlpha(Glyph32 g) +{ + return ISALPHA (TYPE(REAL_GLYPH(g))); +} + +BOOL GUIAPI IsGlyphControl(Glyph32 g) +{ + return TYPE(REAL_GLYPH(g)) == G_UNICODE_CONTROL; +} + +BOOL GUIAPI IsGlyphDigit(Glyph32 g) +{ + return TYPE(REAL_GLYPH(g)) == G_UNICODE_DECIMAL_NUMBER; +} + +BOOL GUIAPI IsGlyphGraph(Glyph32 g) +{ + return !IS (TYPE(REAL_GLYPH(g)), + OR (G_UNICODE_CONTROL, + OR (G_UNICODE_FORMAT, + OR (G_UNICODE_UNASSIGNED, + OR (G_UNICODE_SURROGATE, + OR (G_UNICODE_SPACE_SEPARATOR, + 0)))))); +} + +BOOL GUIAPI IsGlyphLowercase(Glyph32 g) +{ + return TYPE(REAL_GLYPH(g)) == G_UNICODE_LOWERCASE_LETTER; +} + +BOOL GUIAPI IsGlyphPrint(Glyph32 g) +{ + return !IS (TYPE(REAL_GLYPH(g)), + OR (G_UNICODE_CONTROL, + OR (G_UNICODE_FORMAT, + OR (G_UNICODE_UNASSIGNED, + OR (G_UNICODE_SURROGATE, + 0))))); +} + +BOOL GUIAPI IsGlyphUppercase(Glyph32 g) +{ + return TYPE(REAL_GLYPH(g)) == G_UNICODE_UPPERCASE_LETTER; +} + +BOOL GUIAPI IsGlyphPunct(Glyph32 g) +{ + return IS (TYPE(REAL_GLYPH(g)), + OR (G_UNICODE_CONNECT_PUNCTUATION, + OR (G_UNICODE_DASH_PUNCTUATION, + OR (G_UNICODE_CLOSE_PUNCTUATION, + OR (G_UNICODE_FINAL_PUNCTUATION, + OR (G_UNICODE_INITIAL_PUNCTUATION, + OR (G_UNICODE_OTHER_PUNCTUATION, + OR (G_UNICODE_OPEN_PUNCTUATION, + OR (G_UNICODE_CURRENCY_SYMBOL, + OR (G_UNICODE_MODIFIER_SYMBOL, + OR (G_UNICODE_MATH_SYMBOL, + OR (G_UNICODE_OTHER_SYMBOL, + 0)))))))))))); +} + +BOOL GUIAPI IsGlyphSpace(Glyph32 g) +{ + switch (REAL_GLYPH(g)) { + /* special-case these since Unicode thinks they are not spaces */ + case '\t': + case '\n': + case '\r': + case '\f': + return TRUE; + + default: { + if (IS (TYPE(REAL_GLYPH(g)), + OR (G_UNICODE_SPACE_SEPARATOR, + OR (G_UNICODE_LINE_SEPARATOR, + OR (G_UNICODE_PARAGRAPH_SEPARATOR, + 0))))) + return TRUE; + } + break; + } + + return FALSE; +} + +BOOL GUIAPI IsGlyphMark(Glyph32 g) +{ + return ISMARK (TYPE(REAL_GLYPH(g))); +} + +BOOL GUIAPI IsGlyphTitle(Glyph32 g) +{ + unsigned int i; + Glyph32 glyph_value = REAL_GLYPH(g); + + for (i = 0; i < TABLESIZE (title_table); ++i) { + if (title_table[i][0] == glyph_value) { + return TRUE; + } + } + + return FALSE; +} + +BOOL GUIAPI IsGlyphXDigit(Glyph32 g) +{ + Glyph32 glyph_value = REAL_GLYPH(g); + return ((glyph_value >= 'a' && glyph_value <= 'f') + || (glyph_value >= 'A' && glyph_value <= 'F') + || (TYPE(glyph_value) == G_UNICODE_DECIMAL_NUMBER)); +} + +BOOL GUIAPI IsGlyphDefined(Glyph32 g) +{ + return !IS (TYPE(REAL_GLYPH(g)), + OR (G_UNICODE_UNASSIGNED, + OR (G_UNICODE_SURROGATE, + 0))); +} + +BOOL GUIAPI IsGlyphZeroWidth(Glyph32 g) +{ + Glyph32 glyph_value = REAL_GLYPH(g); + if (glyph_value != 0x00AD && ISZEROWIDTHTYPE (TYPE(glyph_value))) + return TRUE; + else if ((glyph_value >= 0x1160 && glyph_value < 0x1200) + || glyph_value == 0x200B) + return TRUE; + + return FALSE; +} + +#define G_WIDTH_TABLE_MIDPOINT (TABLESIZE (g_unicode_width_table_wide) / 2) + +static inline BOOL g_unichar_iswide_bsearch (Glyph32 ch) +{ + unsigned int lower = 0; + unsigned int upper = TABLESIZE (g_unicode_width_table_wide) - 1; + static int saved_mid = G_WIDTH_TABLE_MIDPOINT; + int mid = saved_mid; + + do { + if (ch < g_unicode_width_table_wide[mid].start) + upper = mid - 1; + else if (ch > g_unicode_width_table_wide[mid].end) + lower = mid + 1; + else + return TRUE; + + mid = (lower + upper) / 2; + } + while (lower <= upper); + + return FALSE; +} + +BOOL GUIAPI IsGlyphWide(Glyph32 g) +{ + g = REAL_GLYPH(g); + if (g < g_unicode_width_table_wide[0].start) + return FALSE; + else + return g_unichar_iswide_bsearch (g); +} + +static int interval_compare (const void *key, const void *elt) +{ + Glyph32 g = (Glyph32)((intptr_t)key); + struct Interval *interval = (struct Interval *)elt; + + if (g < interval->start) + return -1; + if (g > interval->end) + return +1; + + return 0; +} + +BOOL GUIAPI IsGlyphWideCJK (Glyph32 g) +{ + g = REAL_GLYPH(g); + if (IsGlyphWide(g)) + return TRUE; + + /* bsearch() is declared attribute(nonnull(1)) so we can't validly search + * for a NULL key */ + if (g == 0) + return FALSE; + + if (bsearch((void*)((intptr_t)g), + g_unicode_width_table_ambiguous, + TABLESIZE (g_unicode_width_table_ambiguous), + sizeof g_unicode_width_table_ambiguous[0], + interval_compare)) + return TRUE; + + return FALSE; +} + +/** + * Converts a character to uppercase. + */ +Glyph32 GlyphToUpper (Glyph32 g) +{ + int t; + + g = REAL_GLYPH(g); + t = TYPE (g); + if (t == G_UNICODE_LOWERCASE_LETTER) { + Glyph32 val = ATTTABLE (g >> 8, g & 0xff); + if (val >= 0x1000000) { + const unsigned char *p = special_case_table + val - 0x1000000; + val = utf8_char_glyph_value (NULL, 0, p, -1); + } + /* Some lowercase letters, e.g., U+000AA, FEMININE ORDINAL INDICATOR, + * do not have an uppercase equivalent, in which case val will be + * zero. + */ + return val ? val : g; + } + else if (t == G_UNICODE_TITLECASE_LETTER) { + unsigned int i; + for (i = 0; i < TABLESIZE (title_table); ++i) { + if (title_table[i][0] == g) + return title_table[i][1] ? title_table[i][1] : g; + } + } + + return g; +} + +/** + * Converts a character to lower case. + */ +Glyph32 GUIAPI GlyphToLower (Glyph32 g) +{ + int t; + + g = REAL_GLYPH(g); + t = TYPE (g); + if (t == G_UNICODE_UPPERCASE_LETTER) { + Glyph32 val = ATTTABLE (g >> 8, g & 0xff); + if (val >= 0x1000000) { + const unsigned char *p = special_case_table + val - 0x1000000; + return utf8_char_glyph_value (NULL, 0, p, -1); + } + else { + /* Not all uppercase letters are guaranteed to have a lowercase + * equivalent. If this is the case, val will be zero. */ + return val ? val : g; + } + } + else if (t == G_UNICODE_TITLECASE_LETTER) { + unsigned int i; + for (i = 0; i < TABLESIZE (title_table); ++i) { + if (title_table[i][0] == g) + return title_table[i][2]; + } + } + return g; +} + +/** + * Converts a glyph to the titlecase. + */ +Glyph32 GUIAPI GlyphToTitle (Glyph32 g) +{ + unsigned int i; + + g = REAL_GLYPH(g); + for (i = 0; i < TABLESIZE (title_table); ++i) { + if (title_table[i][0] == g || title_table[i][1] == g + || title_table[i][2] == g) + return title_table[i][0]; + } + + if (TYPE (g) == G_UNICODE_LOWERCASE_LETTER) + return GlyphToUpper (g); + + return g; +} + +#endif /* _MGCHARSET_UNICODE */ diff --git a/src/font/gunichartables.h b/src/font/gunichartables.h index 04a26a1f..1f91e4a9 100644 --- a/src/font/gunichartables.h +++ b/src/font/gunichartables.h @@ -17585,15 +17585,13 @@ static const unsigned int title_table[][3] = { { 0x1ffc, 0x0000, 0x1ff3 } }; -#if 0 - /* Table of special cases for case conversion; each record contains * First, the best single character mapping to lowercase if Lu, * and to uppercase if Ll, followed by the output mapping for the two cases * other than the case of the codepoint, in the order [Ll],[Lu],[Lt], * encoded in UTF-8, separated and terminated by a null character. */ -static const char special_case_table[] = { +static const unsigned char special_case_table[] = { "\x00\x53\x53\x00\x53\x73\0" /* offset 0 */ "\x69\x69\xcc\x87\x00\xc4\xb0\0" /* offset 7 */ "\x00\x46\x46\x00\x46\x66\0" /* offset 15 */ @@ -17699,7 +17697,7 @@ static const char special_case_table[] = { "\x00\xce\xa9\xcd\x82\xce\x99\x00\xce\xa9\xcd\x82\xcd\x85\0" /* offset 1156 */ }; - +#if 0 /* Table of casefolding cases that can't be derived by lowercasing */ static const struct { @@ -17961,6 +17959,7 @@ static const struct { { 0xfb16, "\xd5\xbe\xd5\xb6" }, { 0xfb17, "\xd5\xb4\xd5\xad" }, }; +#endif // not used struct Interval { @@ -18258,6 +18257,4 @@ static const struct Interval g_unicode_width_table_ambiguous[] = { {0x100000, 0x10FFFD}, }; -#endif /* not used */ - #endif /* CHARTABLES_H */