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: