Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
      3  *
      4  * This is part of HarfBuzz, an OpenType Layout engine library.
      5  *
      6  * Permission is hereby granted, without written agreement and without
      7  * license or royalty fees, to use, copy, modify, and distribute this
      8  * software and its documentation for any purpose, provided that the
      9  * above copyright notice and the following two paragraphs appear in
     10  * all copies of this software.
     11  *
     12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
     13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
     14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
     15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
     16  * DAMAGE.
     17  *
     18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
     19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
     20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
     21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
     22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
     23  */
     24 
     25 #include "harfbuzz-shaper.h"
     26 #include "harfbuzz-shaper-private.h"
     27 
     28 #include <assert.h>
     29 
     30 /*
     31 // Hangul is a syllable based script. Unicode reserves a large range
     32 // for precomposed hangul, where syllables are already precomposed to
     33 // their final glyph shape. In addition, a so called jamo range is
     34 // defined, that can be used to express old Hangul. Modern hangul
     35 // syllables can also be expressed as jamo, and should be composed
     36 // into syllables. The operation is rather simple and mathematical.
     37 
     38 // Every hangul jamo is classified as being either a Leading consonant
     39 // (L), and intermediat Vowel (V) or a trailing consonant (T). Modern
     40 // hangul syllables (the ones in the precomposed area can be of type
     41 // LV or LVT.
     42 //
     43 // Syllable breaks do _not_ occur between:
     44 //
     45 // L              L, V or precomposed
     46 // V, LV          V, T
     47 // LVT, T         T
     48 //
     49 // A standard syllable is of the form L+V+T*. The above rules allow
     50 // nonstandard syllables L*V*T*. To transform them into standard
     51 // syllables fill characters L_f and V_f can be inserted.
     52 */
     53 
     54 enum {
     55     Hangul_SBase = 0xac00,
     56     Hangul_LBase = 0x1100,
     57     Hangul_VBase = 0x1161,
     58     Hangul_TBase = 0x11a7,
     59     Hangul_SCount = 11172,
     60     Hangul_LCount = 19,
     61     Hangul_VCount = 21,
     62     Hangul_TCount = 28,
     63     Hangul_NCount = 21*28
     64 };
     65 
     66 #define hangul_isPrecomposed(uc) \
     67     (uc >= Hangul_SBase && uc < Hangul_SBase + Hangul_SCount)
     68 
     69 #define hangul_isLV(uc) \
     70     ((uc - Hangul_SBase) % Hangul_TCount == 0)
     71 
     72 typedef enum {
     73     L,
     74     V,
     75     T,
     76     LV,
     77     LVT,
     78     X
     79 } HangulType;
     80 
     81 static HangulType hangul_type(unsigned short uc) {
     82     if (uc > Hangul_SBase && uc < Hangul_SBase + Hangul_SCount)
     83         return hangul_isLV(uc) ? LV : LVT;
     84     if (uc < Hangul_LBase || uc > 0x11ff)
     85         return X;
     86     if (uc < Hangul_VBase)
     87         return L;
     88     if (uc < Hangul_TBase)
     89         return V;
     90     return T;
     91 }
     92 
     93 static int hangul_nextSyllableBoundary(const HB_UChar16 *s, int start, int end)
     94 {
     95     const HB_UChar16 *uc = s + start;
     96 
     97     HangulType state = hangul_type(*uc);
     98     int pos = 1;
     99 
    100     while (pos < end - start) {
    101         HangulType newState = hangul_type(uc[pos]);
    102         switch(newState) {
    103         case X:
    104             goto finish;
    105         case L:
    106         case V:
    107         case T:
    108             if (state > newState)
    109                 goto finish;
    110             state = newState;
    111             break;
    112         case LV:
    113             if (state > L)
    114                 goto finish;
    115             state = V;
    116             break;
    117         case LVT:
    118             if (state > L)
    119                 goto finish;
    120             state = T;
    121         }
    122         ++pos;
    123     }
    124 
    125  finish:
    126     return start+pos;
    127 }
    128 
    129 #ifndef NO_OPENTYPE
    130 static const HB_OpenTypeFeature hangul_features [] = {
    131     { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
    132     { HB_MAKE_TAG('l', 'j', 'm', 'o'), CcmpProperty },
    133     { HB_MAKE_TAG('v', 'j', 'm', 'o'), CcmpProperty },
    134     { HB_MAKE_TAG('t', 'j', 'm', 'o'), CcmpProperty },
    135     { 0, 0 }
    136 };
    137 #endif
    138 
    139 static HB_Bool hangul_shape_syllable(HB_ShaperItem *item, HB_Bool openType)
    140 {
    141     const HB_UChar16 *ch = item->string + item->item.pos;
    142     int len = item->item.length;
    143 #ifndef NO_OPENTYPE
    144     const int availableGlyphs = item->num_glyphs;
    145 #endif
    146 
    147     int i;
    148     HB_UChar16 composed = 0;
    149     /* see if we can compose the syllable into a modern hangul */
    150     if (item->item.length == 2) {
    151         int LIndex = ch[0] - Hangul_LBase;
    152         int VIndex = ch[1] - Hangul_VBase;
    153         if (LIndex >= 0 && LIndex < Hangul_LCount &&
    154             VIndex >= 0 && VIndex < Hangul_VCount)
    155             composed = (LIndex * Hangul_VCount + VIndex) * Hangul_TCount + Hangul_SBase;
    156     } else if (item->item.length == 3) {
    157         int LIndex = ch[0] - Hangul_LBase;
    158         int VIndex = ch[1] - Hangul_VBase;
    159         int TIndex = ch[2] - Hangul_TBase;
    160         if (LIndex >= 0 && LIndex < Hangul_LCount &&
    161             VIndex >= 0 && VIndex < Hangul_VCount &&
    162             TIndex >= 0 && TIndex < Hangul_TCount)
    163             composed = (LIndex * Hangul_VCount + VIndex) * Hangul_TCount + TIndex + Hangul_SBase;
    164     }
    165 
    166 
    167 
    168     /* if we have a modern hangul use the composed form */
    169     if (composed) {
    170         ch = &composed;
    171         len = 1;
    172     }
    173 
    174     if (!item->font->klass->convertStringToGlyphIndices(item->font,
    175                                                         ch, len,
    176                                                         item->glyphs, &item->num_glyphs,
    177                                                         item->item.bidiLevel % 2))
    178         return FALSE;
    179     for (i = 0; i < len; i++) {
    180         item->attributes[i].mark = FALSE;
    181         item->attributes[i].clusterStart = FALSE;
    182         item->attributes[i].justification = 0;
    183         item->attributes[i].zeroWidth = FALSE;
    184         /*IDEBUG("    %d: %4x", i, ch[i].unicode()); */
    185     }
    186 
    187 #ifndef NO_OPENTYPE
    188     if (!composed && openType) {
    189         HB_Bool positioned;
    190 
    191         HB_STACKARRAY(unsigned short, logClusters, len);
    192         for (i = 0; i < len; ++i)
    193             logClusters[i] = i;
    194         item->log_clusters = logClusters;
    195 
    196         HB_OpenTypeShape(item, /*properties*/0);
    197 
    198         positioned = HB_OpenTypePosition(item, availableGlyphs, /*doLogClusters*/FALSE);
    199 
    200         HB_FREE_STACKARRAY(logClusters);
    201 
    202         if (!positioned)
    203             return FALSE;
    204     } else {
    205         HB_HeuristicPosition(item);
    206     }
    207 #endif
    208 
    209     item->attributes[0].clusterStart = TRUE;
    210     return TRUE;
    211 }
    212 
    213 HB_Bool HB_HangulShape(HB_ShaperItem *item)
    214 {
    215     const HB_UChar16 *uc = item->string + item->item.pos;
    216     HB_Bool allPrecomposed = TRUE;
    217     int i;
    218 
    219     assert(item->item.script == HB_Script_Hangul);
    220 
    221     for (i = 0; i < (int)item->item.length; ++i) {
    222         if (!hangul_isPrecomposed(uc[i])) {
    223             allPrecomposed = FALSE;
    224             break;
    225         }
    226     }
    227 
    228     if (!allPrecomposed) {
    229         HB_Bool openType = FALSE;
    230         unsigned short *logClusters = item->log_clusters;
    231         HB_ShaperItem syllable;
    232         int first_glyph = 0;
    233         int sstart = item->item.pos;
    234         int end = sstart + item->item.length;
    235 
    236 #ifndef NO_OPENTYPE
    237         openType = HB_SelectScript(item, hangul_features);
    238 #endif
    239         syllable = *item;
    240 
    241         while (sstart < end) {
    242             int send = hangul_nextSyllableBoundary(item->string, sstart, end);
    243 
    244             syllable.item.pos = sstart;
    245             syllable.item.length = send-sstart;
    246             syllable.glyphs = item->glyphs + first_glyph;
    247             syllable.attributes = item->attributes + first_glyph;
    248             syllable.offsets = item->offsets + first_glyph;
    249             syllable.advances = item->advances + first_glyph;
    250             syllable.num_glyphs = item->num_glyphs - first_glyph;
    251             if (!hangul_shape_syllable(&syllable, openType)) {
    252                 item->num_glyphs += syllable.num_glyphs;
    253                 return FALSE;
    254             }
    255             /* fix logcluster array */
    256             for (i = sstart; i < send; ++i)
    257                 logClusters[i-item->item.pos] = first_glyph;
    258             sstart = send;
    259             first_glyph += syllable.num_glyphs;
    260         }
    261         item->num_glyphs = first_glyph;
    262         return TRUE;
    263     }
    264 
    265     return HB_BasicShape(item);
    266 }
    267 
    268 
    269