mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-09-24 00:54:18 +08:00
Emoji, Script, and Width iterators for Unicode string
This commit is contained in:
+11
-4
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+171
-1
@@ -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 */
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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
|
||||
* <http://www.minigui.com/en/about/licensing-policy/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
** 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+60
-17
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user