From 4e67f8c56b81aa209e3d330b6110424adfe901f9 Mon Sep 17 00:00:00 2001 From: Vincent Wei Date: Wed, 23 Jan 2019 14:36:56 +0800 Subject: [PATCH] part implementation of GetGlyphsByRules --- include/gdi.h | 201 +++++++++++---------- src/newgdi/glyph-css.c | 397 ++++++++++++++++++++++++++++++++--------- 2 files changed, 415 insertions(+), 183 deletions(-) diff --git a/include/gdi.h b/include/gdi.h index 87414713..351cd572 100644 --- a/include/gdi.h +++ b/include/gdi.h @@ -5974,18 +5974,18 @@ typedef enum { * @UCHAR_BREAK_HANGUL_T_JAMO: Hangul T Jamo (JT) * @UCHAR_BREAK_HANGUL_LV_SYLLABLE: Hangul LV Syllable (H2) * @UCHAR_BREAK_HANGUL_LVT_SYLLABLE: Hangul LVT Syllable (H3) - * @UCHAR_BREAK_CLOSE_PARANTHESIS: Closing Parenthesis (CP). Since 2.28 - * @UCHAR_BREAK_CONDITIONAL_JAPANESE_STARTER: Conditional Japanese Starter (CJ). Since: 2.32 - * @UCHAR_BREAK_HEBREW_LETTER: Hebrew Letter (HL). Since: 2.32 - * @UCHAR_BREAK_REGIONAL_INDICATOR: Regional Indicator (RI). Since: 2.36 - * @UCHAR_BREAK_EMOJI_BASE: Emoji Base (EB). Since: 2.50 - * @UCHAR_BREAK_EMOJI_MODIFIER: Emoji Modifier (EM). Since: 2.50 - * @UCHAR_BREAK_ZERO_WIDTH_JOINER: Zero Width Joiner (ZWJ). Since: 2.50 + * @UCHAR_BREAK_CLOSE_PARANTHESIS: Closing Parenthesis (CP). + * @UCHAR_BREAK_CONDITIONAL_JAPANESE_STARTER: Conditional Japanese Starter (CJ). + * @UCHAR_BREAK_HEBREW_LETTER: Hebrew Letter (HL). + * @UCHAR_BREAK_REGIONAL_INDICATOR: Regional Indicator (RI). + * @UCHAR_BREAK_EMOJI_BASE: Emoji Base (EB). + * @UCHAR_BREAK_EMOJI_MODIFIER: Emoji Modifier (EM). + * @UCHAR_BREAK_ZERO_WIDTH_JOINER: Zero Width Joiner (ZWJ). * * These are the possible line break classifications. * - * Since new unicode versions may add new types here, applications should be ready - * to handle unknown values. They may be regarded as %UCHAR_BREAK_UNKNOWN. + * Since new unicode versions may add new types here, applications should be + * ready to handle unknown values. They may be regarded as %UCHAR_BREAK_UNKNOWN. * * See [Unicode Line Breaking Algorithm](http://www.unicode.org/unicode/reports/tr14/). */ @@ -6112,94 +6112,88 @@ typedef enum { * @UCHAR_SCRIPT_PHOENICIAN: Phoenician * @UCHAR_SCRIPT_PHAGS_PA: Phags-pa * @UCHAR_SCRIPT_NKO: N'Ko - * @UCHAR_SCRIPT_KAYAH_LI: Kayah Li. Since 2.16.3 - * @UCHAR_SCRIPT_LEPCHA: Lepcha. Since 2.16.3 - * @UCHAR_SCRIPT_REJANG: Rejang. Since 2.16.3 - * @UCHAR_SCRIPT_SUNDANESE: Sundanese. Since 2.16.3 - * @UCHAR_SCRIPT_SAURASHTRA: Saurashtra. Since 2.16.3 - * @UCHAR_SCRIPT_CHAM: Cham. Since 2.16.3 - * @UCHAR_SCRIPT_OL_CHIKI: Ol Chiki. Since 2.16.3 - * @UCHAR_SCRIPT_VAI: Vai. Since 2.16.3 - * @UCHAR_SCRIPT_CARIAN: Carian. Since 2.16.3 - * @UCHAR_SCRIPT_LYCIAN: Lycian. Since 2.16.3 - * @UCHAR_SCRIPT_LYDIAN: Lydian. Since 2.16.3 - * @UCHAR_SCRIPT_AVESTAN: Avestan. Since 2.26 - * @UCHAR_SCRIPT_BAMUM: Bamum. Since 2.26 - * @UCHAR_SCRIPT_EGYPTIAN_HIEROGLYPHS: - * Egyptian Hieroglpyhs. Since 2.26 - * @UCHAR_SCRIPT_IMPERIAL_ARAMAIC: - * Imperial Aramaic. Since 2.26 - * @UCHAR_SCRIPT_INSCRIPTIONAL_PAHLAVI: - * Inscriptional Pahlavi. Since 2.26 - * @UCHAR_SCRIPT_INSCRIPTIONAL_PARTHIAN: - * Inscriptional Parthian. Since 2.26 - * @UCHAR_SCRIPT_JAVANESE: Javanese. Since 2.26 - * @UCHAR_SCRIPT_KAITHI: Kaithi. Since 2.26 - * @UCHAR_SCRIPT_LISU: Lisu. Since 2.26 - * @UCHAR_SCRIPT_MEETEI_MAYEK: - * Meetei Mayek. Since 2.26 - * @UCHAR_SCRIPT_OLD_SOUTH_ARABIAN: - * Old South Arabian. Since 2.26 - * @UCHAR_SCRIPT_OLD_TURKIC: Old Turkic. Since 2.28 - * @UCHAR_SCRIPT_SAMARITAN: Samaritan. Since 2.26 - * @UCHAR_SCRIPT_TAI_THAM: Tai Tham. Since 2.26 - * @UCHAR_SCRIPT_TAI_VIET: Tai Viet. Since 2.26 - * @UCHAR_SCRIPT_BATAK: Batak. Since 2.28 - * @UCHAR_SCRIPT_BRAHMI: Brahmi. Since 2.28 - * @UCHAR_SCRIPT_MANDAIC: Mandaic. Since 2.28 - * @UCHAR_SCRIPT_CHAKMA: Chakma. Since: 2.32 - * @UCHAR_SCRIPT_MEROITIC_CURSIVE: Meroitic Cursive. Since: 2.32 - * @UCHAR_SCRIPT_MEROITIC_HIEROGLYPHS: Meroitic Hieroglyphs. Since: 2.32 - * @UCHAR_SCRIPT_MIAO: Miao. Since: 2.32 - * @UCHAR_SCRIPT_SHARADA: Sharada. Since: 2.32 - * @UCHAR_SCRIPT_SORA_SOMPENG: Sora Sompeng. Since: 2.32 - * @UCHAR_SCRIPT_TAKRI: Takri. Since: 2.32 - * @UCHAR_SCRIPT_BASSA_VAH: Bassa. Since: 2.42 - * @UCHAR_SCRIPT_CAUCASIAN_ALBANIAN: Caucasian Albanian. Since: 2.42 - * @UCHAR_SCRIPT_DUPLOYAN: Duployan. Since: 2.42 - * @UCHAR_SCRIPT_ELBASAN: Elbasan. Since: 2.42 - * @UCHAR_SCRIPT_GRANTHA: Grantha. Since: 2.42 - * @UCHAR_SCRIPT_KHOJKI: Kjohki. Since: 2.42 - * @UCHAR_SCRIPT_KHUDAWADI: Khudawadi, Sindhi. Since: 2.42 - * @UCHAR_SCRIPT_LINEAR_A: Linear A. Since: 2.42 - * @UCHAR_SCRIPT_MAHAJANI: Mahajani. Since: 2.42 - * @UCHAR_SCRIPT_MANICHAEAN: Manichaean. Since: 2.42 - * @UCHAR_SCRIPT_MENDE_KIKAKUI: Mende Kikakui. Since: 2.42 - * @UCHAR_SCRIPT_MODI: Modi. Since: 2.42 - * @UCHAR_SCRIPT_MRO: Mro. Since: 2.42 - * @UCHAR_SCRIPT_NABATAEAN: Nabataean. Since: 2.42 - * @UCHAR_SCRIPT_OLD_NORTH_ARABIAN: Old North Arabian. Since: 2.42 - * @UCHAR_SCRIPT_OLD_PERMIC: Old Permic. Since: 2.42 - * @UCHAR_SCRIPT_PAHAWH_HMONG: Pahawh Hmong. Since: 2.42 - * @UCHAR_SCRIPT_PALMYRENE: Palmyrene. Since: 2.42 - * @UCHAR_SCRIPT_PAU_CIN_HAU: Pau Cin Hau. Since: 2.42 - * @UCHAR_SCRIPT_PSALTER_PAHLAVI: Psalter Pahlavi. Since: 2.42 - * @UCHAR_SCRIPT_SIDDHAM: Siddham. Since: 2.42 - * @UCHAR_SCRIPT_TIRHUTA: Tirhuta. Since: 2.42 - * @UCHAR_SCRIPT_WARANG_CITI: Warang Citi. Since: 2.42 - * @UCHAR_SCRIPT_AHOM: Ahom. Since: 2.48 - * @UCHAR_SCRIPT_ANATOLIAN_HIEROGLYPHS: Anatolian Hieroglyphs. Since: 2.48 - * @UCHAR_SCRIPT_HATRAN: Hatran. Since: 2.48 - * @UCHAR_SCRIPT_MULTANI: Multani. Since: 2.48 - * @UCHAR_SCRIPT_OLD_HUNGARIAN: Old Hungarian. Since: 2.48 - * @UCHAR_SCRIPT_SIGNWRITING: Signwriting. Since: 2.48 - * @UCHAR_SCRIPT_ADLAM: Adlam. Since: 2.50 - * @UCHAR_SCRIPT_BHAIKSUKI: Bhaiksuki. Since: 2.50 - * @UCHAR_SCRIPT_MARCHEN: Marchen. Since: 2.50 - * @UCHAR_SCRIPT_NEWA: Newa. Since: 2.50 - * @UCHAR_SCRIPT_OSAGE: Osage. Since: 2.50 - * @UCHAR_SCRIPT_TANGUT: Tangut. Since: 2.50 - * @UCHAR_SCRIPT_MASARAM_GONDI: Masaram Gondi. Since: 2.54 - * @UCHAR_SCRIPT_NUSHU: Nushu. Since: 2.54 - * @UCHAR_SCRIPT_SOYOMBO: Soyombo. Since: 2.54 - * @UCHAR_SCRIPT_ZANABAZAR_SQUARE: Zanabazar Square. Since: 2.54 - * @UCHAR_SCRIPT_DOGRA: Dogra. Since: 2.58 - * @UCHAR_SCRIPT_GUNJALA_GONDI: Gunjala Gondi. Since: 2.58 - * @UCHAR_SCRIPT_HANIFI_ROHINGYA: Hanifi Rohingya. Since: 2.58 - * @UCHAR_SCRIPT_MAKASAR: Makasar. Since: 2.58 - * @UCHAR_SCRIPT_MEDEFAIDRIN: Medefaidrin. Since: 2.58 - * @UCHAR_SCRIPT_OLD_SOGDIAN: Old Sogdian. Since: 2.58 - * @UCHAR_SCRIPT_SOGDIAN: Sogdian. Since: 2.58 + * @UCHAR_SCRIPT_KAYAH_LI: Kayah Li. + * @UCHAR_SCRIPT_LEPCHA: Lepcha. + * @UCHAR_SCRIPT_REJANG: Rejang. + * @UCHAR_SCRIPT_SUNDANESE: Sundanese. + * @UCHAR_SCRIPT_SAURASHTRA: Saurashtra. + * @UCHAR_SCRIPT_CHAM: Cham. + * @UCHAR_SCRIPT_OL_CHIKI: Ol Chiki. + * @UCHAR_SCRIPT_VAI: Vai. + * @UCHAR_SCRIPT_CARIAN: Carian. + * @UCHAR_SCRIPT_LYCIAN: Lycian. + * @UCHAR_SCRIPT_LYDIAN: Lydian. + * @UCHAR_SCRIPT_AVESTAN: Avestan. + * @UCHAR_SCRIPT_BAMUM: Bamum. + * @UCHAR_SCRIPT_EGYPTIAN_HIEROGLYPHS: Egyptian Hieroglpyhs. + * @UCHAR_SCRIPT_IMPERIAL_ARAMAIC: Imperial Aramaic. + * @UCHAR_SCRIPT_INSCRIPTIONAL_PAHLAVI: Inscriptional Pahlavi. + * @UCHAR_SCRIPT_INSCRIPTIONAL_PARTHIAN: Inscriptional Parthian. + * @UCHAR_SCRIPT_JAVANESE: Javanese. + * @UCHAR_SCRIPT_KAITHI: Kaithi. + * @UCHAR_SCRIPT_LISU: Lisu. + * @UCHAR_SCRIPT_MEETEI_MAYEK: Meetei Mayek. + * @UCHAR_SCRIPT_OLD_SOUTH_ARABIAN: Old South Arabian. + * @UCHAR_SCRIPT_OLD_TURKIC: Old Turkic. + * @UCHAR_SCRIPT_SAMARITAN: Samaritan. + * @UCHAR_SCRIPT_TAI_THAM: Tai Tham. + * @UCHAR_SCRIPT_TAI_VIET: Tai Viet. + * @UCHAR_SCRIPT_BATAK: Batak. + * @UCHAR_SCRIPT_BRAHMI: Brahmi. + * @UCHAR_SCRIPT_MANDAIC: Mandaic. + * @UCHAR_SCRIPT_CHAKMA: Chakma. + * @UCHAR_SCRIPT_MEROITIC_CURSIVE: Meroitic Cursive. + * @UCHAR_SCRIPT_MEROITIC_HIEROGLYPHS: Meroitic Hieroglyphs. + * @UCHAR_SCRIPT_MIAO: Miao. + * @UCHAR_SCRIPT_SHARADA: Sharada. + * @UCHAR_SCRIPT_SORA_SOMPENG: Sora Sompeng. + * @UCHAR_SCRIPT_TAKRI: Takri. + * @UCHAR_SCRIPT_BASSA_VAH: Bassa. + * @UCHAR_SCRIPT_CAUCASIAN_ALBANIAN: Caucasian Albanian. + * @UCHAR_SCRIPT_DUPLOYAN: Duployan. + * @UCHAR_SCRIPT_ELBASAN: Elbasan. + * @UCHAR_SCRIPT_GRANTHA: Grantha. + * @UCHAR_SCRIPT_KHOJKI: Kjohki. + * @UCHAR_SCRIPT_KHUDAWADI: Khudawadi, Sindhi. + * @UCHAR_SCRIPT_LINEAR_A: Linear A. + * @UCHAR_SCRIPT_MAHAJANI: Mahajani. + * @UCHAR_SCRIPT_MANICHAEAN: Manichaean. + * @UCHAR_SCRIPT_MENDE_KIKAKUI: Mende Kikakui. + * @UCHAR_SCRIPT_MODI: Modi. + * @UCHAR_SCRIPT_MRO: Mro. + * @UCHAR_SCRIPT_NABATAEAN: Nabataean. + * @UCHAR_SCRIPT_OLD_NORTH_ARABIAN: Old North Arabian. + * @UCHAR_SCRIPT_OLD_PERMIC: Old Permic. + * @UCHAR_SCRIPT_PAHAWH_HMONG: Pahawh Hmong. + * @UCHAR_SCRIPT_PALMYRENE: Palmyrene. + * @UCHAR_SCRIPT_PAU_CIN_HAU: Pau Cin Hau. + * @UCHAR_SCRIPT_PSALTER_PAHLAVI: Psalter Pahlavi. + * @UCHAR_SCRIPT_SIDDHAM: Siddham. + * @UCHAR_SCRIPT_TIRHUTA: Tirhuta. + * @UCHAR_SCRIPT_WARANG_CITI: Warang Citi. + * @UCHAR_SCRIPT_AHOM: Ahom. + * @UCHAR_SCRIPT_ANATOLIAN_HIEROGLYPHS: Anatolian Hieroglyphs. + * @UCHAR_SCRIPT_HATRAN: Hatran. + * @UCHAR_SCRIPT_MULTANI: Multani. + * @UCHAR_SCRIPT_OLD_HUNGARIAN: Old Hungarian. + * @UCHAR_SCRIPT_SIGNWRITING: Signwriting. + * @UCHAR_SCRIPT_ADLAM: Adlam. + * @UCHAR_SCRIPT_BHAIKSUKI: Bhaiksuki. + * @UCHAR_SCRIPT_MARCHEN: Marchen. + * @UCHAR_SCRIPT_NEWA: Newa. + * @UCHAR_SCRIPT_OSAGE: Osage. + * @UCHAR_SCRIPT_TANGUT: Tangut. + * @UCHAR_SCRIPT_MASARAM_GONDI: Masaram Gondi. + * @UCHAR_SCRIPT_NUSHU: Nushu. + * @UCHAR_SCRIPT_SOYOMBO: Soyombo. + * @UCHAR_SCRIPT_ZANABAZAR_SQUARE: Zanabazar Square. + * @UCHAR_SCRIPT_DOGRA: Dogra. + * @UCHAR_SCRIPT_GUNJALA_GONDI: Gunjala Gondi. + * @UCHAR_SCRIPT_HANIFI_ROHINGYA: Hanifi Rohingya. + * @UCHAR_SCRIPT_MAKASAR: Makasar. + * @UCHAR_SCRIPT_MEDEFAIDRIN: Medefaidrin. + * @UCHAR_SCRIPT_OLD_SOGDIAN: Old Sogdian. + * @UCHAR_SCRIPT_SOGDIAN: Sogdian. * * This enumeration identifies different writing * systems. The values correspond to the names as defined in the @@ -10360,14 +10354,27 @@ static inline int GUIAPI LanguageCodeFromISO639s1Code (const char* iso639_1) /** * The break opportunity code */ +#define BOV_BEFORE_MASK 0xF0 +#define BOV_AFTER_MASK 0x0F + +#define BOV_BEFORE_NOTALLOWED 0x00 +#define BOV_BEFORE_MANDATORY 0x10 +#define BOV_BEFORE_ALLOWED 0x20 + +#define BOV_AFTER_NOTALLOWED 0x00 +#define BOV_AFTER_MANDATORY 0x10 +#define BOV_AFTER_ALLOWED 0x20 + typedef enum { BOV_UNKNOWN, BOV_MANDATORY_AFTER, BOV_MANDATORY_BEFORE, BOV_ALLOWED_AFTER, BOV_ALLOWED_BEFORE, + BOV_ALLOWED_BOTH, BOV_NOT_ALLOWED_AFTER, BOV_NOT_ALLOWED_BEFORE, + BOV_NOT_ALLOWED_BOTH, } BreakOpportunityCode; /** diff --git a/src/newgdi/glyph-css.c b/src/newgdi/glyph-css.c index 21fe9203..8557e7f8 100644 --- a/src/newgdi/glyph-css.c +++ b/src/newgdi/glyph-css.c @@ -66,45 +66,45 @@ #ifdef _MGCHARSET_UNICODE -#define MIN_LEN_GLYPHS 4 -#define INC_LEN_GLYPHS 4 - static int get_next_glyph(DEVFONT* mbc_devfont, DEVFONT* sbc_devfont, - const char* mstr, int mstr_len, Glyph32* g, Uchar32* uc) + const char* mstr, int mstr_len, Glyph32* gv, Uchar32* uc) { - int mchar_len = 0; + int mclen = 0; + + if (mstr_len <= 0) + return 0; if (mbc_devfont) { - mchar_len = mbc_devfont->charset_ops->len_first_char + mclen = mbc_devfont->charset_ops->len_first_char ((const unsigned char*)mstr, mstr_len); - if (mchar_len > 0) { - *g = mbc_devfont->charset_ops->char_glyph_value - (NULL, 0, (Uint8*)mstr, mchar_len); - *g = SET_MBC_GLYPH(*g); + if (mclen > 0) { + *gv = mbc_devfont->charset_ops->char_glyph_value + (NULL, 0, (Uint8*)mstr, mclen); + *gv = SET_MBC_GLYPH(*gv); if (mbc_devfont->charset_ops->conv_to_uc32) - *uc = mbc_devfont->charset_ops->conv_to_uc32(*g); + *uc = mbc_devfont->charset_ops->conv_to_uc32(*gv); else - *uc = GLYPH2UCHAR(*g); + *uc = GLYPH2UCHAR(*gv); } } - if (*g == INV_GLYPH_VALUE) { - mchar_len = sbc_devfont->charset_ops->len_first_char + if (*gv == INV_GLYPH_VALUE) { + mclen = sbc_devfont->charset_ops->len_first_char ((const unsigned char*)mstr, mstr_len); - if (mchar_len > 0) { - *g = sbc_devfont->charset_ops->char_glyph_value - (NULL, 0, (Uint8*)mstr, mchar_len); + if (mclen > 0) { + *gv = sbc_devfont->charset_ops->char_glyph_value + (NULL, 0, (Uint8*)mstr, mclen); if (sbc_devfont->charset_ops->conv_to_uc32) - *uc = sbc_devfont->charset_ops->conv_to_uc32(*g); + *uc = sbc_devfont->charset_ops->conv_to_uc32(*gv); else - *uc = GLYPH2UCHAR(*g); + *uc = GLYPH2UCHAR(*gv); } } - return mchar_len; + return mclen; } static UCharBreakType resolve_line_breaking_class( @@ -152,18 +152,18 @@ static int collapse_space(DEVFONT* mbc_devfont, DEVFONT* sbc_devfont, int cosumed = 0; do { - int mchar_len; - Glyph32 g; + int mclen; + Glyph32 gv; Uchar32 uc; - mchar_len = get_next_glyph(mbc_devfont, sbc_devfont, mstr, mstr_len, - &g, &uc); - if (mchar_len == 0) + mclen = get_next_glyph(mbc_devfont, sbc_devfont, mstr, mstr_len, + &gv, &uc); + if (mclen == 0) break; - mstr += mchar_len; - mstr_len -= mchar_len; - cosumed += mchar_len; + mstr += mclen; + mstr_len -= mclen; + cosumed += mclen; bt = UCharGetBreak(uc); } while (bt == UCHAR_BREAK_SPACE); @@ -171,17 +171,124 @@ static int collapse_space(DEVFONT* mbc_devfont, DEVFONT* sbc_devfont, return cosumed; } +static int collapse_line_feed(DEVFONT* mbc_devfont, DEVFONT* sbc_devfont, + const char* mstr, int mstr_len) +{ + int cosumed = 0; + int mclen; + Glyph32 gv; + Uchar32 uc; + + mclen = get_next_glyph(mbc_devfont, sbc_devfont, mstr, mstr_len, + &gv, &uc); + if (mclen > 0 && UCharGetBreak(uc) == UCHAR_BREAK_LINE_FEED) + cosumed = mclen; + + return cosumed; +} + +static BOOL is_next_glyph_zw(DEVFONT* mbc_devfont, DEVFONT* sbc_devfont, + const char* mstr, int mstr_len) +{ + int mclen; + Glyph32 gv; + Uchar32 uc; + + mclen = get_next_glyph(mbc_devfont, sbc_devfont, mstr, mstr_len, + &gv, &uc); + if (mclen > 0 && UCharGetBreak(uc) == UCHAR_BREAK_ZERO_WIDTH_SPACE) + return TRUE; + + return FALSE; +} + +static int is_next_glyph_cm_or_zwj(DEVFONT* mbc_devfont, DEVFONT* sbc_devfont, + const char* mstr, int mstr_len, Glyph32* gv, Uchar32* uc) +{ + int mclen; + + mclen = get_next_glyph(mbc_devfont, sbc_devfont, mstr, mstr_len, + gv, uc); + if (mclen > 0) { + UCharBreakType bt = UCharGetBreak(*uc); + if (bt == UCHAR_BREAK_COMBINING_MARK + || bt == UCHAR_BREAK_ZERO_WIDTH_JOINER) + return mclen; + } + + return 0; +} + +#define MIN_LEN_GLYPHS 4 +#define INC_LEN_GLYPHS 4 + +struct glyph_break_info { + int len_buff; + int n; + Glyph32* gs; + Uint8* bs; +}; + +static int gbinfo_init(struct glyph_break_info* gbinfo, int size) +{ + // pre-allocate buffers + gbinfo->len_buff = size; + if (gbinfo->len_buff < MIN_LEN_GLYPHS) + gbinfo->len_buff = MIN_LEN_GLYPHS; + + gbinfo->gs = (Glyph32*)malloc(sizeof(Glyph32) * gbinfo->len_buff); + gbinfo->bs = (Uint8*)malloc(sizeof(Uint8) * gbinfo->len_buff); + if (gbinfo->gs == NULL || gbinfo->bs == NULL) + return 0; + + return gbinfo->len_buff; +} + +static int gbinfo_push_back(struct glyph_break_info* gbinfo, Glyph32 gv, Uint8 bt) +{ + /* realloc buffers if it needs */ + if ((gbinfo->n + 2) >= gbinfo->len_buff) { + gbinfo->len_buff += INC_LEN_GLYPHS; + gbinfo->gs = (Glyph32*)realloc(gbinfo->gs, sizeof(Glyph32) * gbinfo->len_buff); + gbinfo->bs = (Uint8*)realloc(gbinfo->bs, sizeof(Uint8) * gbinfo->len_buff); + if (gbinfo->gs == NULL || gbinfo->bs == NULL) + return 0; + } + + gbinfo->gs[gbinfo->n] = gv; + gbinfo->bs[gbinfo->n] = bt; + gbinfo->n++; + return gbinfo->n; +} + +static int check_subsequent_cm_or_zwj(DEVFONT* mbc_devfont, DEVFONT* sbc_devfont, + const char* mstr, int mstr_len, struct glyph_break_info* gbinfo) +{ + int cosumed = 0; + int mclen; + Glyph32 gv; + Uchar32 uc; + + while ((mclen = is_next_glyph_cm_or_zwj(mbc_devfont, sbc_devfont, + mstr, mstr_len, &gv, &uc)) > 0) { + + // FIXME: CM/ZWJ should have the same break class as + // its base character. + gbinfo_push_back(gbinfo, gv, BOV_NOT_ALLOWED_BEFORE); + + cosumed += mclen; + } + + return cosumed; +} + int GUIAPI GetGlyphsByRules(LOGFONT* logfont, const char* mstr, int mstr_len, LanguageCode content_language, UCharScriptType writing_system, Uint32 space_rule, Uint32 trans_rule, Glyph32** glyphs, Uint8** break_oppos, int* nr_glyphs) { - Glyph32* gs = NULL; - Uint8* bs = NULL; - int len_buff; - int n = 0; + struct glyph_break_info gbinfo = {0, 0, NULL, NULL}; int cosumed = 0; - DEVFONT* sbc_devfont = logfont->sbc_devfont; DEVFONT* mbc_devfont = logfont->mbc_devfont; @@ -191,85 +298,203 @@ int GUIAPI GetGlyphsByRules(LOGFONT* logfont, const char* mstr, int mstr_len, if (mstr_len == 0) return 0; - // pre-allocate buffers - len_buff = mstr_len >> 1; - if (len_buff < MIN_LEN_GLYPHS) - len_buff = MIN_LEN_GLYPHS; - - gs = (Glyph32*)malloc(sizeof(Glyph32) * len_buff); - bs = (Uint8*)malloc(sizeof(Uint8) * len_buff); - if (gs == NULL || bs == NULL) + if (gbinfo_init(&gbinfo, mstr_len >> 1) <= 0) { goto error; + } - while (mstr_len > 0 && *mstr != '\0') { - Glyph32 g = INV_GLYPH_VALUE; - Uchar32 uc = 0; - int mchar_len; + while (mstr_len > 0) { + Glyph32 gv; + Uchar32 uc; + int mclen; UCharBasicType gc; UCharBreakType bt; - mchar_len = get_next_glyph(mbc_devfont, sbc_devfont, mstr, mstr_len, - &g, &uc); - if (mchar_len == 0) + Glyph32 next_gv; + Uchar32 next_uc; + UCharBasicType next_gc; + UCharBreakType next_bt; + int next_mclen; + + mclen = get_next_glyph(mbc_devfont, sbc_devfont, mstr, mstr_len, + &gv, &uc); + if (mclen == 0) { + // badly encoded or end of text break; - - gc = UCharGetType(uc); - bt = UCharGetBreak(uc); - - // CSS: collapses space acoording to space rule - if ((space_rule == WSR_NORMAL || space_rule == WSR_NOWRAP) - && bt == UCHAR_BREAK_SPACE) { - mchar_len += collapse_space(mbc_devfont, sbc_devfont, mstr, - mstr_len); } - mstr_len -= mchar_len; - mstr += mchar_len; - cosumed += mchar_len; - - gs[n] = g; - bs[n] = BOV_UNKNOWN; - if (space_rule == WSR_PRE || space_rule == WSR_NOWRAP) { - // only break at forced line breaks. + // TODO: only break at forced line breaks. } else { - /* Complete Line Breaking Algorithm goes here */ - - // LB1: Resolve line breaking classes - bt = resolve_line_breaking_class(content_language, writing_system, - gc, bt); - // mark all breaking opportunities } - /* check and realloc buffers */ - n++; - if (n == len_buff) { - len_buff += INC_LEN_GLYPHS; - gs = (Glyph32*)realloc(gs, sizeof(Glyph32) * len_buff); - bs = (Uint8*)realloc(bs, sizeof(Uint8) * len_buff); - if (gs == NULL || bs == NULL) + // LB1 Resolve line breaking class + gc = UCharGetType(uc); + bt = UCharGetBreak(uc); + bt = resolve_line_breaking_class(content_language, writing_system, + gc, bt); + + // LB2 Never break at the start of text. + if (gbinfo.n == 0) { + Uint8 bo = BOV_BEFORE_NOTALLOWED; + if (gbinfo_push_back(&gbinfo, gv, bo) == 0) + goto error; + } + // LB3 Always break at the end of text. + else if (*mstr == '\0') { + break; + } + // LB4 Always break after hard line breaks + // LB6 Do not break before hard line breaks. + else if (bt == UCHAR_BREAK_MANDATORY) { + Uint8 bo = BOV_AFTER_MANDATORY | BOV_BEFORE_NOTALLOWED; + if (gbinfo_push_back(&gbinfo, gv, bo) == 0) + goto error; + } + // LB5 Treat CR followed by LF, as well as CR, LF, + // and NL as hard line breaks. + // LB6 Do not break before hard line breaks. + else if (bt == UCHAR_BREAK_CARRIAGE_RETURN) { + mclen += collapse_line_feed(mbc_devfont, sbc_devfont, mstr, + mstr_len); + Uint8 bo = BOV_AFTER_MANDATORY | BOV_BEFORE_NOTALLOWED; + if (gbinfo_push_back(&gbinfo, gv, bo) == 0) + goto error; + } + // LB5 Treat CR followed by LF, as well as CR, LF, + // and NL as hard line breaks. + // LB6 Do not break before hard line breaks. + else if (bt == UCHAR_BREAK_LINE_FEED + || bt == UCHAR_BREAK_NEXT_LINE) { + Uint8 bo = BOV_AFTER_MANDATORY | BOV_BEFORE_NOTALLOWED; + if (gbinfo_push_back(&gbinfo, gv, bo) == 0) + goto error; + } + // LB7: Do not break before spaces or zero width space. + else if (bt == UCHAR_BREAK_SPACE) { + Uint8 bo = BOV_BEFORE_NOTALLOWED; + if (gbinfo_push_back(&gbinfo, gv, bo) == 0) + goto error; + + if (space_rule == WSR_NORMAL || space_rule == WSR_NOWRAP) { + // CSS: collapses space according to space rule + mclen += collapse_space(mbc_devfont, sbc_devfont, mstr, + mstr_len); + } + } + // LB7: Do not break before spaces or zero width space. + else if (bt == UCHAR_BREAK_ZERO_WIDTH_SPACE) { + Uint8 bo = BOV_BEFORE_NOTALLOWED; + if (gbinfo_push_back(&gbinfo, gv, bo) == 0) + goto error; + } + // LB8: Break before any character following a zero-width space, + // even if one or more spaces intervene. + else if (is_next_glyph_zw(mbc_devfont, sbc_devfont, mstr, mstr_len)) { + Uint8 bo = BOV_BEFORE_MANDATORY; + if (gbinfo_push_back(&gbinfo, gv, bo) == 0) + goto error; + } + // LB8a Do not break between a zero width joiner and an ideograph, + // emoji base or emoji modifier. + else if (bt == UCHAR_BREAK_ZERO_WIDTH_JOINER) { + next_mclen = get_next_glyph(mbc_devfont, sbc_devfont, + mstr, mstr_len, &next_gv, &next_uc); + Uint8 bo; + + if (next_mclen > 0) { + next_gc = UCharGetType(next_uc); + next_bt = UCharGetBreak(next_uc); + next_bt = resolve_line_breaking_class(content_language, + writing_system, next_gc, next_bt); + if (next_bt == UCHAR_BREAK_IDEOGRAPHIC + || next_bt == UCHAR_BREAK_EMOJI_BASE + || next_bt == UCHAR_BREAK_EMOJI_MODIFIER) { + + mclen += next_mclen; + + bo = BOV_AFTER_NOTALLOWED; + if (gbinfo_push_back(&gbinfo, gv, bo) == 0) + goto error; + + bo = BOV_BEFORE_NOTALLOWED; + if (gbinfo_push_back(&gbinfo, next_gv, bo) == 0) + goto error; + } + } + } + // LB9: Do not break a combining character sequence; + // treat it as if it has the line breaking class of + // the base character in all of the following rules. + // Treat ZWJ as if it were CM. + else if (bt != UCHAR_BREAK_MANDATORY + && bt != UCHAR_BREAK_CARRIAGE_RETURN + && bt != UCHAR_BREAK_LINE_FEED + && bt != UCHAR_BREAK_NEXT_LINE + && bt != UCHAR_BREAK_SPACE + && bt != UCHAR_BREAK_ZERO_WIDTH_SPACE + && (next_mclen = is_next_glyph_cm_or_zwj(mbc_devfont, + sbc_devfont, mstr, mstr_len, &next_gv, &next_uc)) > 0) { + Uint8 bo; + + mclen += next_mclen; + + bo = BOV_AFTER_NOTALLOWED; + if (gbinfo_push_back(&gbinfo, gv, bo) == 0) + goto error; + + // FIXME: CM/ZWJ should have the same break class as + // its base character. + bo = BOV_BEFORE_NOTALLOWED; + if (gbinfo_push_back(&gbinfo, next_gv, bo) == 0) + goto error; + + // For any possible subsequent CM/ZWJ characters. + mclen += check_subsequent_cm_or_zwj(mbc_devfont, + sbc_devfont, mstr + next_mclen, mstr_len - next_mclen, + &gbinfo); + + } + // LB10 Treat any remaining combining mark or ZWJ as AL. + else if (bt == UCHAR_BREAK_COMBINING_MARK + || bt == UCHAR_BREAK_ZERO_WIDTH_JOINER) { + bt = UCHAR_BREAK_ALPHABETIC; + } + // LB11 Do not break before or after Word joiner + // and related characters. + else if (bt == UCHAR_BREAK_WORD_JOINER) { + Uint8 bo = BOV_BEFORE_NOTALLOWED | BOV_AFTER_NOTALLOWED; + if (gbinfo_push_back(&gbinfo, gv, bo) == 0) goto error; } + mstr_len -= mclen; + mstr += mclen; + cosumed += mclen; + + // Return if we got any BK! + if ((gbinfo.bs[gbinfo.n] & BOV_AFTER_MASK) == BOV_AFTER_MANDATORY) { + break; + } } - if (n == 0) { - if (gs) free(gs); - if (bs) free(bs); + if (gbinfo.n == 0) { + if (gbinfo.gs) free(gbinfo.gs); + if (gbinfo.bs) free(gbinfo.bs); + return 0; } else { - *glyphs = gs; - *break_oppos = bs; - *nr_glyphs = n; + *glyphs = gbinfo.gs; + *break_oppos = gbinfo.bs; + *nr_glyphs = gbinfo.n; } return cosumed; error: - if (gs) free(gs); - if (bs) free(bs); + if (gbinfo.gs) free(gbinfo.gs); + if (gbinfo.bs) free(gbinfo.bs); return 0; }