| /* |
| * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |
| * |
| * This is part of HarfBuzz, an OpenType Layout engine library. |
| * |
| * Permission is hereby granted, without written agreement and without |
| * license or royalty fees, to use, copy, modify, and distribute this |
| * software and its documentation for any purpose, provided that the |
| * above copyright notice and the following two paragraphs appear in |
| * all copies of this software. |
| * |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
| * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
| * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
| * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| * |
| * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
| * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
| * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
| * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
| * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
| */ |
| |
| #include "harfbuzz-shaper.h" |
| #include "harfbuzz-shaper-private.h" |
| |
| #include <assert.h> |
| |
| /* |
| // Hangul is a syllable based script. Unicode reserves a large range |
| // for precomposed hangul, where syllables are already precomposed to |
| // their final glyph shape. In addition, a so called jamo range is |
| // defined, that can be used to express old Hangul. Modern hangul |
| // syllables can also be expressed as jamo, and should be composed |
| // into syllables. The operation is rather simple and mathematical. |
| |
| // Every hangul jamo is classified as being either a Leading consonant |
| // (L), and intermediat Vowel (V) or a trailing consonant (T). Modern |
| // hangul syllables (the ones in the precomposed area can be of type |
| // LV or LVT. |
| // |
| // Syllable breaks do _not_ occur between: |
| // |
| // L L, V or precomposed |
| // V, LV V, T |
| // LVT, T T |
| // |
| // A standard syllable is of the form L+V+T*. The above rules allow |
| // nonstandard syllables L*V*T*. To transform them into standard |
| // syllables fill characters L_f and V_f can be inserted. |
| */ |
| |
| enum { |
| Hangul_SBase = 0xac00, |
| Hangul_LBase = 0x1100, |
| Hangul_VBase = 0x1161, |
| Hangul_TBase = 0x11a7, |
| Hangul_SCount = 11172, |
| Hangul_LCount = 19, |
| Hangul_VCount = 21, |
| Hangul_TCount = 28, |
| Hangul_NCount = 21*28 |
| }; |
| |
| #define hangul_isPrecomposed(uc) \ |
| (uc >= Hangul_SBase && uc < Hangul_SBase + Hangul_SCount) |
| |
| #define hangul_isLV(uc) \ |
| ((uc - Hangul_SBase) % Hangul_TCount == 0) |
| |
| typedef enum { |
| L, |
| V, |
| T, |
| LV, |
| LVT, |
| X |
| } HangulType; |
| |
| static HangulType hangul_type(unsigned short uc) { |
| if (uc > Hangul_SBase && uc < Hangul_SBase + Hangul_SCount) |
| return hangul_isLV(uc) ? LV : LVT; |
| if (uc < Hangul_LBase || uc > 0x11ff) |
| return X; |
| if (uc < Hangul_VBase) |
| return L; |
| if (uc < Hangul_TBase) |
| return V; |
| return T; |
| } |
| |
| static int hangul_nextSyllableBoundary(const HB_UChar16 *s, int start, int end) |
| { |
| const HB_UChar16 *uc = s + start; |
| |
| HangulType state = hangul_type(*uc); |
| int pos = 1; |
| |
| while (pos < end - start) { |
| HangulType newState = hangul_type(uc[pos]); |
| switch(newState) { |
| case X: |
| goto finish; |
| case L: |
| case V: |
| case T: |
| if (state > newState) |
| goto finish; |
| state = newState; |
| break; |
| case LV: |
| if (state > L) |
| goto finish; |
| state = V; |
| break; |
| case LVT: |
| if (state > L) |
| goto finish; |
| state = T; |
| } |
| ++pos; |
| } |
| |
| finish: |
| return start+pos; |
| } |
| |
| #ifndef NO_OPENTYPE |
| static const HB_OpenTypeFeature hangul_features [] = { |
| { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty }, |
| { HB_MAKE_TAG('l', 'j', 'm', 'o'), CcmpProperty }, |
| { HB_MAKE_TAG('v', 'j', 'm', 'o'), CcmpProperty }, |
| { HB_MAKE_TAG('t', 'j', 'm', 'o'), CcmpProperty }, |
| { 0, 0 } |
| }; |
| #endif |
| |
| static HB_Bool hangul_shape_syllable(HB_ShaperItem *item, HB_Bool openType) |
| { |
| const HB_UChar16 *ch = item->string + item->item.pos; |
| int len = item->item.length; |
| #ifndef NO_OPENTYPE |
| const int availableGlyphs = item->num_glyphs; |
| #endif |
| |
| int i; |
| HB_UChar16 composed = 0; |
| /* see if we can compose the syllable into a modern hangul */ |
| if (item->item.length == 2) { |
| int LIndex = ch[0] - Hangul_LBase; |
| int VIndex = ch[1] - Hangul_VBase; |
| if (LIndex >= 0 && LIndex < Hangul_LCount && |
| VIndex >= 0 && VIndex < Hangul_VCount) |
| composed = (LIndex * Hangul_VCount + VIndex) * Hangul_TCount + Hangul_SBase; |
| } else if (item->item.length == 3) { |
| int LIndex = ch[0] - Hangul_LBase; |
| int VIndex = ch[1] - Hangul_VBase; |
| int TIndex = ch[2] - Hangul_TBase; |
| if (LIndex >= 0 && LIndex < Hangul_LCount && |
| VIndex >= 0 && VIndex < Hangul_VCount && |
| TIndex >= 0 && TIndex < Hangul_TCount) |
| composed = (LIndex * Hangul_VCount + VIndex) * Hangul_TCount + TIndex + Hangul_SBase; |
| } |
| |
| |
| |
| /* if we have a modern hangul use the composed form */ |
| if (composed) { |
| ch = &composed; |
| len = 1; |
| } |
| |
| if (!item->font->klass->convertStringToGlyphIndices(item->font, |
| ch, len, |
| item->glyphs, &item->num_glyphs, |
| item->item.bidiLevel % 2)) |
| return FALSE; |
| for (i = 0; i < len; i++) { |
| item->attributes[i].mark = FALSE; |
| item->attributes[i].clusterStart = FALSE; |
| item->attributes[i].justification = 0; |
| item->attributes[i].zeroWidth = FALSE; |
| /*IDEBUG(" %d: %4x", i, ch[i].unicode()); */ |
| } |
| |
| #ifndef NO_OPENTYPE |
| if (!composed && openType) { |
| HB_Bool positioned; |
| |
| HB_STACKARRAY(unsigned short, logClusters, len); |
| for (i = 0; i < len; ++i) |
| logClusters[i] = i; |
| item->log_clusters = logClusters; |
| |
| HB_OpenTypeShape(item, /*properties*/0); |
| |
| positioned = HB_OpenTypePosition(item, availableGlyphs, /*doLogClusters*/FALSE); |
| |
| HB_FREE_STACKARRAY(logClusters); |
| |
| if (!positioned) |
| return FALSE; |
| } else { |
| HB_HeuristicPosition(item); |
| } |
| #endif |
| |
| item->attributes[0].clusterStart = TRUE; |
| return TRUE; |
| } |
| |
| HB_Bool HB_HangulShape(HB_ShaperItem *item) |
| { |
| const HB_UChar16 *uc = item->string + item->item.pos; |
| HB_Bool allPrecomposed = TRUE; |
| int i; |
| |
| assert(item->item.script == HB_Script_Hangul); |
| |
| for (i = 0; i < (int)item->item.length; ++i) { |
| if (!hangul_isPrecomposed(uc[i])) { |
| allPrecomposed = FALSE; |
| break; |
| } |
| } |
| |
| if (!allPrecomposed) { |
| HB_Bool openType = FALSE; |
| unsigned short *logClusters = item->log_clusters; |
| HB_ShaperItem syllable; |
| int first_glyph = 0; |
| int sstart = item->item.pos; |
| int end = sstart + item->item.length; |
| |
| #ifndef NO_OPENTYPE |
| openType = HB_SelectScript(item, hangul_features); |
| #endif |
| syllable = *item; |
| |
| while (sstart < end) { |
| int send = hangul_nextSyllableBoundary(item->string, sstart, end); |
| |
| syllable.item.pos = sstart; |
| syllable.item.length = send-sstart; |
| syllable.glyphs = item->glyphs + first_glyph; |
| syllable.attributes = item->attributes + first_glyph; |
| syllable.offsets = item->offsets + first_glyph; |
| syllable.advances = item->advances + first_glyph; |
| syllable.num_glyphs = item->num_glyphs - first_glyph; |
| if (!hangul_shape_syllable(&syllable, openType)) { |
| item->num_glyphs += syllable.num_glyphs; |
| return FALSE; |
| } |
| /* fix logcluster array */ |
| for (i = sstart; i < send; ++i) |
| logClusters[i-item->item.pos] = first_glyph; |
| sstart = send; |
| first_glyph += syllable.num_glyphs; |
| } |
| item->num_glyphs = first_glyph; |
| return TRUE; |
| } |
| |
| return HB_BasicShape(item); |
| } |
| |
| |