diff --git a/include/gdi.h b/include/gdi.h index 4ca51ff3..7e107eab 100644 --- a/include/gdi.h +++ b/include/gdi.h @@ -12410,9 +12410,14 @@ typedef enum { GLYPH_ORIENT_EAST = GLYPH_GRAVITY_EAST, GLYPH_ORIENT_WEST = GLYPH_GRAVITY_WEST, GLYPH_ORIENT_SOUTH = GLYPH_GRAVITY_SOUTH, - GLYPH_ORIENT_MIXED, } GlyphOrient; +typedef enum { + GLYPH_ORIENT_POLICY_NATURAL, + GLYPH_ORIENT_POLICY_STRONG, + GLYPH_ORIENT_POLICY_LINE, +} GlyphOrientPolicy; + /** * Split a Uchar32 paragraph string into text runs according to the * scripts of characters. @@ -12421,9 +12426,11 @@ typedef enum { * breaking opportunities of the string. */ MG_EXPORT GLYPHRUNINFO* GUIAPI CreateGlyphRunInfo(Uchar32* ucs, int nr_ucs, - const char* lang_tag, const char* script_tag, - GlyphRunDir run_dir, GlyphOrient glyph_orient, ParagraphDir base_dir, - Uint8 ctr, Uint8 wbr, Uint8 lbp, LOGFONT* logfont, RGBCOLOR color); + LanguageCode lang_code, ScriptType script_type, + ParagraphDir base_dir, GlyphRunDir run_dir, + GlyphOrient glyph_orient, GlyphOrientPolicy orient_policy, + Uint8 ctr, Uint8 wbr, Uint8 lbp, + LOGFONT* logfont, RGBCOLOR color); /** * Set font of part characters. Please call this function before diff --git a/src/font/Makefile.am b/src/font/Makefile.am index c115c8f6..cc3d3f2b 100644 --- a/src/font/Makefile.am +++ b/src/font/Makefile.am @@ -16,7 +16,8 @@ SRC_FILES = charset.c charset-arabic.c legacy-bidi.c charset-unicode.c \ unicode-bidi.c \ unicode-break.c \ unicode-joining-types.c unicode-joining.c unicode-shape.c \ - unicode-shape-arabic.c harzbuff-minigui-funcs.c + unicode-shape-arabic.c harzbuff-minigui-funcs.c \ + unicode-iterators.c HDR_FILES = charset.h rawbitmap.h varbitmap.h freetype2.h qpf.h se_minigui.h \ upf.h bitmapfont.h unicode-bidi-tables.h \ @@ -34,7 +35,8 @@ HDR_FILES = charset.h rawbitmap.h varbitmap.h freetype2.h qpf.h se_minigui.h \ unicode-bidi-brackets-table.inc \ unicode-bidi-brackets-type-table.inc \ unicode-bidi-mirroring-table.inc \ - script-language-table.inc + script-language-table.inc \ + emoji_presentation_scanner.inc EXTRA_DIST = convgbmap.c jisunimap.c $(SRC_FILES) $(HDR_FILES) \ makefile.ng makefile.msvc diff --git a/src/font/emoji_presentation_scanner.inc b/src/font/emoji_presentation_scanner.inc new file mode 100644 index 00000000..9e9fb02b --- /dev/null +++ b/src/font/emoji_presentation_scanner.inc @@ -0,0 +1,284 @@ + +#line 1 "emoji_presentation_scanner.rl" +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +#line 9 "emoji_presentation_scanner.c" +static const char _emoji_presentation_actions[] = { + 0, 1, 0, 1, 1, 1, 5, 1, + 6, 1, 7, 1, 8, 1, 9, 1, + 10, 1, 11, 2, 2, 3, 2, 2, + 4 +}; + +static const char _emoji_presentation_key_offsets[] = { + 0, 5, 7, 14, 18, 20, 21, 24, + 29, 30, 34, 36 +}; + +static const unsigned char _emoji_presentation_trans_keys[] = { + 3u, 7u, 13u, 0u, 2u, 14u, 15u, 2u, + 3u, 6u, 7u, 13u, 0u, 1u, 9u, 10u, + 11u, 12u, 10u, 12u, 10u, 4u, 10u, 12u, + 4u, 9u, 10u, 11u, 12u, 6u, 9u, 10u, + 11u, 12u, 8u, 10u, 9u, 10u, 11u, 12u, + 14u, 0 +}; + +static const char _emoji_presentation_single_lengths[] = { + 3, 2, 5, 4, 2, 1, 3, 5, + 1, 4, 2, 5 +}; + +static const char _emoji_presentation_range_lengths[] = { + 1, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; + +static const char _emoji_presentation_index_offsets[] = { + 0, 5, 8, 15, 20, 23, 25, 29, + 35, 37, 42, 45 +}; + +static const char _emoji_presentation_indicies[] = { + 2, 1, 1, 1, 0, 4, 5, 3, + 7, 8, 10, 11, 12, 6, 9, 5, + 13, 14, 15, 0, 13, 15, 16, 13, + 16, 15, 13, 15, 16, 15, 5, 13, + 14, 15, 16, 5, 17, 5, 13, 14, + 18, 17, 5, 13, 16, 5, 13, 14, + 15, 4, 16, 0 +}; + +static const char _emoji_presentation_trans_targs[] = { + 2, 4, 6, 2, 1, 2, 3, 3, + 7, 2, 8, 9, 11, 0, 2, 5, + 2, 2, 10 +}; + +static const char _emoji_presentation_trans_actions[] = { + 17, 19, 19, 15, 0, 7, 22, 19, + 19, 9, 0, 22, 19, 0, 5, 19, + 11, 13, 19 +}; + +static const char _emoji_presentation_to_state_actions[] = { + 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; + +static const char _emoji_presentation_from_state_actions[] = { + 0, 0, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; + +static const char _emoji_presentation_eof_trans[] = { + 1, 4, 0, 1, 17, 17, 17, 17, + 18, 18, 17, 17 +}; + +static const int emoji_presentation_start = 2; + +static const int emoji_presentation_en_text_and_emoji_run = 2; + + +#line 9 "emoji_presentation_scanner.rl" + + + +#line 78 "emoji_presentation_scanner.rl" + + +static emoji_text_iter_t +scan_emoji_presentation (emoji_text_iter_t p, + const emoji_text_iter_t pe, + bool* is_emoji) +{ + emoji_text_iter_t ts, te; + const emoji_text_iter_t eof = pe; + + unsigned act; + int cs; + + +#line 107 "emoji_presentation_scanner.c" + { + cs = emoji_presentation_start; + ts = 0; + te = 0; + act = 0; + } + +#line 115 "emoji_presentation_scanner.c" + { + int _klen; + unsigned int _trans; + const char *_acts; + unsigned int _nacts; + const unsigned char *_keys; + + if ( p == pe ) + goto _test_eof; +_resume: + _acts = _emoji_presentation_actions + _emoji_presentation_from_state_actions[cs]; + _nacts = (unsigned int) *_acts++; + while ( _nacts-- > 0 ) { + switch ( *_acts++ ) { + case 1: +#line 1 "NONE" + {ts = p;} + break; +#line 134 "emoji_presentation_scanner.c" + } + } + + _keys = _emoji_presentation_trans_keys + _emoji_presentation_key_offsets[cs]; + _trans = _emoji_presentation_index_offsets[cs]; + + _klen = _emoji_presentation_single_lengths[cs]; + if ( _klen > 0 ) { + const unsigned char *_lower = _keys; + const unsigned char *_mid; + const unsigned char *_upper = _keys + _klen - 1; + while (1) { + if ( _upper < _lower ) + break; + + _mid = _lower + ((_upper-_lower) >> 1); + if ( (*p) < *_mid ) + _upper = _mid - 1; + else if ( (*p) > *_mid ) + _lower = _mid + 1; + else { + _trans += (unsigned int)(_mid - _keys); + goto _match; + } + } + _keys += _klen; + _trans += _klen; + } + + _klen = _emoji_presentation_range_lengths[cs]; + if ( _klen > 0 ) { + const unsigned char *_lower = _keys; + const unsigned char *_mid; + const unsigned char *_upper = _keys + (_klen<<1) - 2; + while (1) { + if ( _upper < _lower ) + break; + + _mid = _lower + (((_upper-_lower) >> 1) & ~1); + if ( (*p) < _mid[0] ) + _upper = _mid - 2; + else if ( (*p) > _mid[1] ) + _lower = _mid + 2; + else { + _trans += (unsigned int)((_mid - _keys)>>1); + goto _match; + } + } + _trans += _klen; + } + +_match: + _trans = _emoji_presentation_indicies[_trans]; +_eof_trans: + cs = _emoji_presentation_trans_targs[_trans]; + + if ( _emoji_presentation_trans_actions[_trans] == 0 ) + goto _again; + + _acts = _emoji_presentation_actions + _emoji_presentation_trans_actions[_trans]; + _nacts = (unsigned int) *_acts++; + while ( _nacts-- > 0 ) + { + switch ( *_acts++ ) + { + case 2: +#line 1 "NONE" + {te = p+1;} + break; + case 3: +#line 74 "emoji_presentation_scanner.rl" + {act = 2;} + break; + case 4: +#line 75 "emoji_presentation_scanner.rl" + {act = 3;} + break; + case 5: +#line 73 "emoji_presentation_scanner.rl" + {te = p+1;{ *is_emoji = false; return te; }} + break; + case 6: +#line 74 "emoji_presentation_scanner.rl" + {te = p+1;{ *is_emoji = true; return te; }} + break; + case 7: +#line 75 "emoji_presentation_scanner.rl" + {te = p+1;{ *is_emoji = false; return te; }} + break; + case 8: +#line 74 "emoji_presentation_scanner.rl" + {te = p;p--;{ *is_emoji = true; return te; }} + break; + case 9: +#line 75 "emoji_presentation_scanner.rl" + {te = p;p--;{ *is_emoji = false; return te; }} + break; + case 10: +#line 74 "emoji_presentation_scanner.rl" + {{p = ((te))-1;}{ *is_emoji = true; return te; }} + break; + case 11: +#line 1 "NONE" + { switch( act ) { + case 2: + {{p = ((te))-1;} *is_emoji = true; return te; } + break; + case 3: + {{p = ((te))-1;} *is_emoji = false; return te; } + break; + } + } + break; +#line 248 "emoji_presentation_scanner.c" + } + } + +_again: + _acts = _emoji_presentation_actions + _emoji_presentation_to_state_actions[cs]; + _nacts = (unsigned int) *_acts++; + while ( _nacts-- > 0 ) { + switch ( *_acts++ ) { + case 0: +#line 1 "NONE" + {ts = 0;} + break; +#line 261 "emoji_presentation_scanner.c" + } + } + + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _emoji_presentation_eof_trans[cs] > 0 ) { + _trans = _emoji_presentation_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + } + +#line 94 "emoji_presentation_scanner.rl" + + + /* Should not be reached. */ + ts = ts + 0; // suppress compilation warning. + *is_emoji = false; + return pe; +} diff --git a/src/font/unicode-emoji.c b/src/font/unicode-emoji.c index b747fd30..6fa40add 100644 --- a/src/font/unicode-emoji.c +++ b/src/font/unicode-emoji.c @@ -35,7 +35,7 @@ /* ** Some operators to check Emoji. ** Implementation of the functions is based on some code from HarfBuzz -** (licensed under MIT).: +** (licensed under MIT): ** ** https://github.com/harfbuzz/harfbuzz ** @@ -107,4 +107,174 @@ BOOL _unicode_is_regional_indicator(Uchar32 ch) return (ch >= 0x1F1E6 && ch <= 0x1F1FF); } +/* + * Implementation of EmojiIterator is based on Chromium's Ragel-based + * parser: + * + * https://chromium-review.googlesource.com/c/chromium/src/+/1264577 + * + * The grammar file emoji_presentation_scanner.rl was just modified to + * adapt the function signature and variables to our usecase. The + * grammar itself was NOT modified: + * + * https://chromium-review.googlesource.com/c/chromium/src/+/1264577/3/third_party/blink/renderer/platform/fonts/emoji_presentation_scanner.rl + * + * The emoji_presentation_scanner.c is generated from .rl file by + * running ragel on it. + * + * The categorization is also based on: + * + * https://chromium-review.googlesource.com/c/chromium/src/+/1264577/3/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h + * + * The iterator next() is based on: + * + * https://chromium-review.googlesource.com/c/chromium/src/+/1264577/3/third_party/blink/renderer/platform/fonts/symbols_iterator.cc + * + * // Copyright 2015 The Chromium Authors. All rights reserved. + * // Use of this source code is governed by a BSD-style license that can be + * // found in the LICENSE file. + */ + +const Uchar32 kCombiningEnclosingCircleBackslashCharacter = 0x20E0; +const Uchar32 kCombiningEnclosingKeycapCharacter = 0x20E3; +const Uchar32 kVariationSelector15Character = 0xFE0E; +const Uchar32 kVariationSelector16Character = 0xFE0F; +const Uchar32 kZeroWidthJoinerCharacter = 0x200D; + +enum EmojiScannerCategory { + EMOJI = 0, + EMOJI_TEXT_PRESENTATION = 1, + EMOJI_EMOJI_PRESENTATION = 2, + EMOJI_MODIFIER_BASE = 3, + EMOJI_MODIFIER = 4, + EMOJI_VS_BASE = 5, + REGIONAL_INDICATOR = 6, + KEYCAP_BASE = 7, + COMBINING_ENCLOSING_KEYCAP = 8, + COMBINING_ENCLOSING_CIRCLE_BACKSLASH = 9, + ZWJ = 10, + VS15 = 11, + VS16 = 12, + TAG_BASE = 13, + TAG_SEQUENCE = 14, + TAG_TERM = 15, + kMaxEmojiScannerCategory = 16 +}; + +static unsigned char emojiSegmentationCategory (Uchar32 codepoint) +{ + /* Specific ones first. */ + if (codepoint == kCombiningEnclosingKeycapCharacter) + return COMBINING_ENCLOSING_KEYCAP; + if (codepoint == kCombiningEnclosingCircleBackslashCharacter) + return COMBINING_ENCLOSING_CIRCLE_BACKSLASH; + if (codepoint == kZeroWidthJoinerCharacter) + return ZWJ; + if (codepoint == kVariationSelector15Character) + return VS15; + if (codepoint == kVariationSelector16Character) + return VS16; + if (codepoint == 0x1F3F4) + return TAG_BASE; + if ((codepoint >= 0xE0030 && codepoint <= 0xE0039) || + (codepoint >= 0xE0061 && codepoint <= 0xE007A)) + return TAG_SEQUENCE; + if (codepoint == 0xE007F) + return TAG_TERM; + + if (_unicode_is_emoji_modifier_base (codepoint)) + return EMOJI_MODIFIER_BASE; + if (_unicode_is_emoji_modifier (codepoint)) + return EMOJI_MODIFIER; + if (_unicode_is_regional_indicator (codepoint)) + return REGIONAL_INDICATOR; + if (_unicode_is_emoji_keycap_base (codepoint)) + return KEYCAP_BASE; + + if (_unicode_is_emoji_emoji_default (codepoint)) + return EMOJI_EMOJI_PRESENTATION; + if (_unicode_is_emoji_text_default (codepoint)) + return EMOJI_TEXT_PRESENTATION; + if (_unicode_is_emoji (codepoint)) + return EMOJI; + + /* Ragel state machine will interpret unknown category as "any". */ + return kMaxEmojiScannerCategory; +} + + +typedef BOOL bool; +enum { false = FALSE, true = TRUE }; +typedef unsigned char *emoji_text_iter_t; + +#include "emoji_presentation_scanner.inc" + +EmojiIterator * __mg_emoji_iter_init (EmojiIterator *iter, + const Uchar32* ucs, int nr_ucs) +{ + int i; + unsigned char *types; + const Uchar32 *p; + + assert (nr_ucs > 0); + + types = malloc (sizeof (unsigned char) * nr_ucs); + if (types == NULL) + return NULL; + + p = ucs; + for (i = 0; i < nr_ucs; i++) { + types[i] = emojiSegmentationCategory (*p); + p++; + } + + iter->text_start = iter->start = iter->end = ucs; + iter->text_end = ucs + nr_ucs; + iter->is_emoji = FALSE; + + iter->types = types; + iter->n_chars = nr_ucs; + iter->cursor = 0; + + __mg_emoji_iter_next (iter); + return iter; +} + +BOOL __mg_emoji_iter_next (EmojiIterator *iter) +{ + unsigned int old_cursor, cursor; + BOOL is_emoji; + + if (iter->end >= iter->text_end) + return FALSE; + + iter->start = iter->end; + + old_cursor = cursor = iter->cursor; + cursor = scan_emoji_presentation (iter->types + cursor, + iter->types + iter->n_chars, + &is_emoji) - iter->types; + + do { + iter->cursor = cursor; + iter->is_emoji = is_emoji; + + if (cursor == iter->n_chars) + break; + + cursor = scan_emoji_presentation (iter->types + cursor, + iter->types + iter->n_chars, + &is_emoji) - iter->types; + } + while (iter->is_emoji == is_emoji); + + iter->end = iter->start + (iter->cursor - old_cursor); + return TRUE; +} + +void __mg_emoji_iter_fini (EmojiIterator *iter) +{ + free (iter->types); +} + #endif /* _MGCHARSET_UNICODE */ diff --git a/src/font/unicode-iterators.c b/src/font/unicode-iterators.c new file mode 100644 index 00000000..9f05a263 --- /dev/null +++ b/src/font/unicode-iterators.c @@ -0,0 +1,315 @@ +/* + * This file is part of MiniGUI, a mature cross-platform windowing + * and Graphics User Interface (GUI) support system for embedded systems + * and smart IoT devices. + * + * Copyright (C) 2002~2018, Beijing FMSoft Technologies Co., Ltd. + * Copyright (C) 1998~2002, WEI Yongming + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Or, + * + * As this program is a library, any link to this program must follow + * GNU General Public License version 3 (GPLv3). If you cannot accept + * GPLv3, you need to be licensed from FMSoft. + * + * If you have got a commercial license of this program, please use it + * under the terms and conditions of the commercial license. + * + * For more information about the commercial license, please refer to + * . + */ + +/* +** ucs-iterator.c: The implementation of the uchar string iterators. +** +** Create by WEI Yongming at 2019/03/18 +** +** Implementation of WidthIterator is derived from LGPL'd Pango. +** +** Implementation of ScriptIterator is derived from ICU: +** +** icu/sources/common/usc_impl.c +** +*********************************************************************** +** Copyright (C) 1999-2002, International Business Machines +** Corporation and others. All Rights Reserved. +*********************************************************************** +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, and/or sell copies of the Software, and to permit persons +** to whom the Software is furnished to do so, provided that the above +** copyright notice(s) and this permission notice appear in all copies of +** the Software and that both the above copyright notice(s) and this +** permission notice appear in supporting documentation. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +** OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +** HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL +** INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING +** FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +** NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +** WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +** +** Except as contained in this notice, the name of a copyright holder +** shall not be used in advertising or otherwise to promote the sale, use +** or other dealings in this Software without prior written authorization +** of the copyright holder. +*/ + +#include +#include +#include + +#include "common.h" + +#if defined(_MGCHARSET_UNICODE) + +#include "minigui.h" +#include "gdi.h" +#include "unicode-ops.h" + +ScriptIterator* __mg_script_iterator_init(ScriptIterator *iter, + const Uchar32* ucs, int nr_ucs) +{ + assert (nr_ucs >= 0); + + iter->ucs_start = ucs; + iter->ucs_end = ucs + nr_ucs; + iter->script_start = ucs; + iter->script_end = ucs; + iter->script_type = SCRIPT_COMMON; + + iter->paren_sp = -1; + + __mg_script_iterator_next (iter); + return iter; +} + +static const Uchar32 paired_chars[] = { + 0x0028, 0x0029, /* ascii paired punctuation */ + 0x003c, 0x003e, + 0x005b, 0x005d, + 0x007b, 0x007d, + 0x00ab, 0x00bb, /* guillemets */ + 0x2018, 0x2019, /* general punctuation */ + 0x201c, 0x201d, + 0x2039, 0x203a, + 0x3008, 0x3009, /* chinese paired punctuation */ + 0x300a, 0x300b, + 0x300c, 0x300d, + 0x300e, 0x300f, + 0x3010, 0x3011, + 0x3014, 0x3015, + 0x3016, 0x3017, + 0x3018, 0x3019, + 0x301a, 0x301b +}; + +static int get_pair_index (Uchar32 ch) +{ + int lower = 0; + int upper = TABLESIZE (paired_chars) - 1; + + while (lower <= upper) { + int mid = (lower + upper) / 2; + + if (ch < paired_chars[mid]) + upper = mid - 1; + else if (ch > paired_chars[mid]) + lower = mid + 1; + else + return mid; + } + + return -1; +} + +#define REAL_SCRIPT(script) \ + ((script) > SCRIPT_INHERITED && (script) != SCRIPT_UNKNOWN) + +#define SAME_SCRIPT(script1, script2) \ + (!REAL_SCRIPT (script1) || !REAL_SCRIPT (script2) || (script1) == (script2)) + +#define IS_OPEN(pair_index) (((pair_index) & 1) == 0) + +/** + * __mg_script_iterator_next: + * + * Advances a #ScriptIterator to the next range. If @iter + * is already at the end, it is left unchanged and %FALSE + * is returned. + * + * Return value: %TRUE if @iter was successfully advanced. + **/ +BOOL __mg_script_iterator_next (ScriptIterator *iter) +{ + int start_sp; + + if (iter->script_end == iter->ucs_end) + return FALSE; + + start_sp = iter->paren_sp; + iter->script_type = SCRIPT_COMMON; + iter->script_start = iter->script_end; + + for (; iter->script_end < iter->ucs_end; iter->script_end++) { + Uchar32 ch = *iter->script_end; + ScriptType sc; + int pair_index; + + sc = UCharGetScriptType(ch); + if (sc != SCRIPT_COMMON) + pair_index = -1; + else + pair_index = get_pair_index (ch); + + /* + * Paired character handling: + * + * if it's an open character, push it onto the stack. + * if it's a close character, find the matching open on the + * stack, and use that script code. Any non-matching open + * characters above it on the stack will be poped. + */ + if (pair_index >= 0) { + if (IS_OPEN (pair_index)) { + /* + * If the paren stack is full, empty it. This + * means that deeply nested paired punctuation + * characters will be ignored, but that's an unusual + * case, and it's better to ignore them than to + * write off the end of the stack... + */ + if (++iter->paren_sp >= PAREN_STACK_DEPTH) + iter->paren_sp = 0; + + iter->paren_stack[iter->paren_sp].pair_index = pair_index; + iter->paren_stack[iter->paren_sp].script_type = + iter->script_type; + } + else if (iter->paren_sp >= 0) { + int pi = pair_index & ~1; + + while (iter->paren_sp >= 0 && + iter->paren_stack[iter->paren_sp].pair_index != pi) + iter->paren_sp--; + + if (iter->paren_sp < start_sp) + start_sp = iter->paren_sp; + + if (iter->paren_sp >= 0) + sc = iter->paren_stack[iter->paren_sp].script_type; + } + } + + if (SAME_SCRIPT (iter->script_type, sc)) { + if (!REAL_SCRIPT (iter->script_type) && REAL_SCRIPT (sc)) { + iter->script_type = sc; + + /* + * now that we have a final script code, fix any open + * characters we pushed before we knew the script code. + */ + while (start_sp < iter->paren_sp) + iter->paren_stack[++start_sp].script_type = + iter->script_type; + } + + /* + * if this character is a close paired character, + * pop it from the stack + */ + if (pair_index >= 0 && !IS_OPEN (pair_index) && + iter->paren_sp >= 0) { + iter->paren_sp--; + + if (iter->paren_sp < start_sp) + start_sp = iter->paren_sp; + } + } + else { + /* Different script, we're done */ + break; + } + } + + return TRUE; +} + +static inline BOOL width_iter_is_upright(Uchar32 ch) +{ + return (UCharGetVerticalOrientation(ch) == UCHAR_VOP_U); +} + +void __mg_width_iter_next(WidthIterator* iter) +{ + BOOL met_joiner = FALSE; + iter->start = iter->end; + + if (iter->end < iter->ucs_end) { + iter->upright = width_iter_is_upright(*iter->end); + } + + while (iter->end < iter->ucs_end) { + Uchar32 ch = *iter->end; + + /* for zero width joiner */ + if (ch == 0x200D) { + iter->end++; + met_joiner = TRUE; + continue; + } + + /* ignore the upright check if met joiner */ + if (met_joiner) { + iter->end++; + met_joiner = FALSE; + continue; + } + + /* for variation selector, tag and emoji modifier. */ + if (ch == 0xFE0EU || ch == 0xFE0FU || + (ch >= 0xE0020 && ch <= 0xE007F) || + (ch >= 0x1F3FB && ch <= 0x1F3FF)) { + iter->end++; + continue; + } + + if (width_iter_is_upright (ch) != iter->upright) + break; + + iter->end++; + } +} + +WidthIterator* __mg_width_iter_init (WidthIterator* iter, + const Uchar32* ucs, int nr_ucs) +{ + iter->ucs_start = ucs; + iter->ucs_end = ucs + nr_ucs; + iter->start = iter->end = ucs; + + __mg_width_iter_next (iter); + return iter; +} + +#endif /* _MGCHARSET_UNICODE */ + diff --git a/src/include/unicode-ops.h b/src/include/unicode-ops.h index 99406da3..19bd6fc3 100644 --- a/src/include/unicode-ops.h +++ b/src/include/unicode-ops.h @@ -48,6 +48,44 @@ #define UCHAR_IDSPACE 0x3000 #define UCHAR_TAB 0x0009 +#define PAREN_STACK_DEPTH 128 + +typedef struct _ParenStackEntry { + int pair_index; + ScriptType script_type; +} ParenStackEntry; + +typedef struct _ScriptIterator { + const Uchar32* ucs_start; + const Uchar32* ucs_end; + const Uchar32* script_start; + const Uchar32* script_end; + int paren_sp; + + ScriptType script_type; + ParenStackEntry paren_stack[PAREN_STACK_DEPTH]; +} ScriptIterator; + +typedef struct _WidthIterator { + const Uchar32* ucs_start; + const Uchar32* ucs_end; + const Uchar32* start; + const Uchar32* end; + BOOL upright; +} WidthIterator; + +typedef struct _EmojiIterator { + const Uchar32 *text_start; + const Uchar32 *text_end; + const Uchar32 *start; + const Uchar32 *end; + BOOL is_emoji; + + unsigned char *types; + unsigned int n_chars; + unsigned int cursor; +} EmojiIterator; + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -81,6 +119,19 @@ BOOL _unicode_is_emoji_emoji_default(Uchar32 ch); BOOL _unicode_is_emoji_keycap_base(Uchar32 ch); BOOL _unicode_is_regional_indicator(Uchar32 ch); +ScriptIterator*__mg_script_iterator_init (ScriptIterator *iter, + const Uchar32* ucs, int nr_ucs); +BOOL __mg_script_iterator_next (ScriptIterator* iter); + +WidthIterator* __mg_width_iter_init (WidthIterator* iter, + const Uchar32* ucs, int nr_ucs); +void __mg_width_iter_next(WidthIterator* iter); + +EmojiIterator* __mg_emoji_iter_init (EmojiIterator *iter, + const Uchar32* ucs, int nr_ucs); +BOOL __mg_emoji_iter_next (EmojiIterator *iter); +void __mg_emoji_iter_fini (EmojiIterator *iter); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/newgdi/Makefile.am b/src/newgdi/Makefile.am index 605b5d7f..a6df028f 100644 --- a/src/newgdi/Makefile.am +++ b/src/newgdi/Makefile.am @@ -17,7 +17,8 @@ SRC_FILES = gdi.c attr.c clip.c map.c coor.c rect.c \ text.c achar-uchar.c glyph.c legacy-bidi.c \ textout.c tabbedtextout.c drawtext.c \ glyph-unicode.c \ - glyphruninfo.c shape-glyphs-basic.c shape-glyphs-complex.c \ + glyphruninfo.c \ + shape-glyphs-basic.c shape-glyphs-complex.c \ layout-shaped-glyphs.c endif diff --git a/src/newgdi/glyphruninfo.c b/src/newgdi/glyphruninfo.c index 67d7b5ff..10e575b3 100644 --- a/src/newgdi/glyphruninfo.c +++ b/src/newgdi/glyphruninfo.c @@ -53,23 +53,68 @@ #include "unicode-ops.h" #include "glyphruninfo.h" -/* Local array size, used for stack-based local arrays */ +typedef enum { + EMBEDDING_CHANGED = 1 << 0, + SCRIPT_CHANGED = 1 << 1, + LANG_CHANGED = 1 << 2, + DERIVED_LANG_CHANGED = 1 << 3, + WIDTH_CHANGED = 1 << 3, + EMOJI_CHANGED = 1 << 5, +} ChangedFlags; +typedef struct _GlyphRunState { + GLYPHRUNINFO* context; + const Uchar32* text; + const Uchar32* end; + + const Uchar32* run_start; + const Uchar32* run_end; + + BidiLevel* embedding_levels; + const Uchar32* embedding_end; + int embedding_end_offset; + BidiLevel embedding; + + GlyphOrient orient; + GlyphOrientPolicy orient_policy; + GlyphOrient resolved_gravity; + BOOL centered_baseline; + + Uint8 changed; + + ScriptIterator script_iter; + const Uchar32* script_end; + ScriptType script; + + WidthIterator width_iter; + EmojiIterator emoji_iter; + + LanguageCode derived_lang; +} GlyphRunState; + +static BOOL init_glyph_runs(GLYPHRUNINFO* runinfo) +{ +#if 0 + BidiLevel el = runinfo->els[0]; + ScriptType st = UCharGetScriptType(runinfo->ucs[0]); +#endif + + return FALSE; +} + +/* Local array size, used for stack-based local arrays */ #if SIZEOF_PTR == 8 # define LOCAL_ARRAY_SIZE 256 #else # define LOCAL_ARRAY_SIZE 128 #endif -static BOOL init_glyph_runs(GLYPHRUNINFO* runinfo) -{ - return FALSE; -} - GLYPHRUNINFO* GUIAPI CreateGlyphRunInfo(Uchar32* ucs, int nr_ucs, - const char* lang_tag, const char* script_tag, - GlyphRunDir run_dir, GlyphOrient glyph_orient, ParagraphDir base_dir, - Uint8 ctr, Uint8 wbr, Uint8 lbp, LOGFONT* logfont, RGBCOLOR color) + LanguageCode lang_code, ScriptType script_type, + ParagraphDir base_dir, GlyphRunDir run_dir, + GlyphOrient glyph_orient, GlyphOrientPolicy orient_policy, + Uint8 ctr, Uint8 wbr, Uint8 lbp, + LOGFONT* logfont, RGBCOLOR color) { BOOL ok = FALSE; @@ -84,11 +129,8 @@ GLYPHRUNINFO* GUIAPI CreateGlyphRunInfo(Uchar32* ucs, int nr_ucs, BidiBracketType local_brk_ts[LOCAL_ARRAY_SIZE]; BidiBracketType *brk_ts = NULL; - BidiLevel level_or, level_and; - ScriptType script_type = SCRIPT_COMMON; - int i, j; runinfo = (GLYPHRUNINFO*)calloc(1, sizeof(GLYPHRUNINFO)); @@ -99,7 +141,7 @@ GLYPHRUNINFO* GUIAPI CreateGlyphRunInfo(Uchar32* ucs, int nr_ucs, if (nr_ucs < LOCAL_ARRAY_SIZE) bidi_ts = local_bidi_ts; else - bidi_ts = malloc (nr_ucs * sizeof(BidiType)); + bidi_ts = malloc(nr_ucs * sizeof(BidiType)); if (!bidi_ts) { _DBG_PRINTF("%s: failed to allocate space for bidi types.\n"); @@ -141,6 +183,10 @@ GLYPHRUNINFO* GUIAPI CreateGlyphRunInfo(Uchar32* ucs, int nr_ucs, } // Initialize other fields + runinfo->ucs = ucs; + runinfo->els = els; + runinfo->bos = bos; + INIT_LIST_HEAD(&runinfo->cm_head.list); runinfo->cm_head.si = 0; runinfo->cm_head.len = nr_ucs; @@ -154,7 +200,7 @@ GLYPHRUNINFO* GUIAPI CreateGlyphRunInfo(Uchar32* ucs, int nr_ucs, runinfo->run_head.si = 0; runinfo->run_head.nr_ucs = nr_ucs; - runinfo->run_head.lc = LanguageCodeFromISO639s1Code(lang_tag); + runinfo->run_head.lc = lang_code; runinfo->run_head.st = script_type; runinfo->run_head.level = BIDI_DIR_TO_LEVEL(base_dir); runinfo->run_head.dir = run_dir; @@ -185,9 +231,6 @@ GLYPHRUNINFO* GUIAPI CreateGlyphRunInfo(Uchar32* ucs, int nr_ucs, goto out; } - runinfo->ucs = ucs; - runinfo->els = els; - runinfo->bos = bos; ok = TRUE; out: