text: /*
   * 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);
  }
  
  
  