diff options
Diffstat (limited to 'third_party/harfbuzz/src/harfbuzz-hangul.c')
-rw-r--r-- | third_party/harfbuzz/src/harfbuzz-hangul.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/third_party/harfbuzz/src/harfbuzz-hangul.c b/third_party/harfbuzz/src/harfbuzz-hangul.c new file mode 100644 index 0000000..a819dac --- /dev/null +++ b/third_party/harfbuzz/src/harfbuzz-hangul.c @@ -0,0 +1,268 @@ +/* + * 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('j', '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); +} + + |