All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.fontengine.inlineformatting.infontformatting.ThaiFormatter Maven / Gradle / Ivy

/*
*
*	File: ThaiFormatter.java
*
*
*	ADOBE CONFIDENTIAL
*	___________________
*
*	Copyright 2004-2005 Adobe Systems Incorporated
*	All Rights Reserved.
*
*	NOTICE: All information contained herein is, and remains the property of
*	Adobe Systems Incorporated and its suppliers, if any. The intellectual
*	and technical concepts contained herein are proprietary to Adobe Systems
*	Incorporated and its suppliers and may be covered by U.S. and Foreign
*	Patents, patents in process, and are protected by trade secret or
*	copyright law. Dissemination of this information or reproduction of this
*	material is strictly forbidden unless prior written permission is obtained
*	from Adobe Systems Incorporated.
*
*/
package com.adobe.fontengine.inlineformatting.infontformatting;

import com.adobe.fontengine.font.FontLoadingException;
import com.adobe.fontengine.font.InvalidFontException;
import com.adobe.fontengine.font.UnsupportedFontException;
import com.adobe.fontengine.font.opentype.OpenTypeFont;
import com.adobe.fontengine.inlineformatting.AttributedRun;
import com.adobe.fontengine.inlineformatting.ElementAttribute;

final class ThaiFormatter extends GenericFormatter {
  
  //------------------------------------------------------------- firstPass ---
  
  /** Expand occurrences of SARA AM into NIKHAHIT and SARA AA.
   */
  protected int removeSaraAm (AttributedRun run, int start, int limit) {
    // The only difficulty is that we want to place the NIKHAHIT in
    // front of any tone mark that may precede the SARA AM. 
    // We have to do that because most fonts treat the NIKAHIT 
    // and tone mark as "marks above" without further distinction,
    // and the NIKHAHIT of a SARA AM must appear below those tone
    // marks.
    int cur = start;
    while (cur < limit) {
      int usv = run.elementAt (cur);
      if (usv == 0x0E33 /* U+0E33 THAI CHARACTER SARA AM */) {
        int prev = cur;
        while (   start <= prev - 1 
               && run.getElementStyle (prev-1, ElementAttribute.isGlyph) != Boolean.TRUE
               && 0x0E48 <= run.elementAt (prev-1) /* U+0E48 THAI CHARACTER MAI EK */
               && run.elementAt (prev-1) <= 0xE4B /* U+0E4B THAI CHARACTER MAI CHATTAWA */) {
          prev--; }
        
        if (prev == cur) {
          // common case without tone marks
          run.replace (cur, new int[] {0x0E4D, 0x0E32}); }
        
        else {
          int [] positions = new int [cur - prev + 1];
          for (int i = 0; i < positions.length; i++) {
            positions [i] = prev + i; }
          int [] elementIds = new int [cur - prev + 2];
          elementIds [0] = 0x0E4D;
          for (int i = 1; i < elementIds.length - 1; i++) {
            elementIds [i] = run.elementAt (prev + i - 1); }
          elementIds [elementIds.length - 1] = 0x0E32;
          run.replace (positions, elementIds); }
 
        cur++;
        limit++; }
      
      cur++; }
    
    return limit;
  }

  public int firstPass (AttributedRun run, int start, int limit) {
    limit =  removeSaraAm  (run, start, limit);
    return super.firstPass (run, start, limit);
  }
  

  //-------------------------------------------------------------- formatTT ---
  //
  // Up to and including Windows XP SP2, the fonts provided with Windows
  // for Thai do not use OpenType layout. Instead, the layout is achieved
  // by having extra glyphs in the font, mapped from the code points 
  // in the range U+F700 - U+F71F, and it is up to the layout engine to 
  // fetch the appropriate glyphs. This method replaces the characters in
  // a run with characters in the PUA range as appropriate.
  // 
  // The conditions under which to use these extra glyphs have been determined
  // by looking at the position of the their ink relative to their origin. There
  // is unfortunately not specification to build from. In particular, the use
  // of the glyphs mapped from U+F71B, U+F71C, U+F71D remains a mystery.
  
  protected void shapeTT (AttributedRun run, int start, int limit) {
    
    // The shaper is essentially a finite state machine, driven by the
    // input characters. Actually, we only need to distinguish a few
    // character classes:
    
    final int OT = 0; // other
    final int C0 = 1; // consonant, no ascender, no descender
    final int CA = 2; // consonant, ascender
    final int CD = 3; // consonant, descender
    final int CR = 4; // consonant, removable piece below
    final int BASE = 5;
    final int VA = 6; // vowel above
    final int VB = 7; // vowel below
    final int DA = 8; // tone or diacritic above
    
    final int[] charClass = {
        /*         0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f */
        /* E0x */ OT, C0, C0, C0, C0, C0, C0, C0, C0, C0, C0, C0, C0, CR, CD, CD,
        /* E1x */ CR, C0, C0, C0, C0, C0, C0, C0, C0, C0, C0, CA, C0, CA, C0, CA,
        /* E2x */ C0, C0, C0, C0, CD, C0, CD, C0, C0, C0, C0, C0, CA, C0, C0, OT,
        /* E3x */ OT, VA, OT, OT, VA, VA, VA, VA, VB, VB, VB, OT, OT, OT, OT, OT,
        /* E4x */ OT, OT, OT, OT, OT, OT, OT, VA, DA, DA, DA, DA, DA, DA, DA, OT};
       
    // The vowels above mapped from the U+0Exx code points have their ink
    // positionned for the case where they sit on top of a consonant without
    // an ascender.
    //
    // The diacritics above mapped from the U+0Exx code points have their ink
    // positionned for the case where they sit on top of a vowel above, on
    // a consonant without an ascender.
    // 
    // The diacritics below mapped from the U+0Exx code points have their ing
    // positionned for the case where they sit below a consonant without a 
    // descended.
    // 
    // The consonants with a removable piece below mapped from the U+0Exx
    // code point have that piece below
    //
    // The operations of the finite state machine are to select
    // alternate glyphs, via alternate entries in the cmap, in the appriate
    // conditions.
    
    
    // This operation moves vowels and diacritics above towards the left, when
    // they sit on a consonant with ascender. (Of course, this moves nothing,
    // but rather selects an alternate glyph where the ink is more to the left)
    
    final int[]  shiftLeft = {
      /* E3x */  0x0E30, 0xf710, 0x0E32, 0x0E33, 0xf701, 0xf702, 0xf703, 0xf704,
                 0x0E38, 0x0E39, 0x0E3A, 0x0E3B, 0x0E3C, 0x0E3D, 0x0E3E, 0x0E3F,
      /* E4x */  0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0xf712,
                 0xf713, 0xf714, 0xf715, 0xf716, 0xf717, 0xf711, 0x0E4E, 0x0E4F };
    

    // This operation moves diacritics above down, when there is no vowel on
    // a ascender-less consonant; and it moves diacritics below down, when
    // they are on a consonant with descender.
    
    final int[]  shiftDown = {
      /* E3x */  0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,
                 0xf718, 0xf719, 0xf71a, 0x0E3B, 0x0E3C, 0x0E3D, 0x0E3E, 0x0E3F,
      /* E4x */  0xE040, 0xE041, 0xE042, 0xE043, 0xE044, 0xE045, 0xE046, 0xE047,
                 0xf70a, 0xf70b, 0xf70c, 0xf70d, 0xf70e, 0x0E4D, 0x0E4E, 0x0E4F };
    
    // This operation moves diacritics above down and left, when there is no 
    // vowel on a consonant with an ascender

    final int[]  shiftLeftAndDown = {
      /* E3x */  0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,
                 0x0E38, 0x0E39, 0x0E3A, 0x0E3B, 0x0E3C, 0x0E3D, 0x0E3E, 0x0E3F,
      /* E4x */  0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47,
                 0xf705, 0xf706, 0xf707, 0xf708, 0xf709, 0x0E4D, 0x0E4E, 0x0E4F };

    // This operation removes the piece below a consonant, when there is 
    // a diacritic below.
    
    final int[]  removePieceUnder = {
      /* E0x */  0x0E00, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07,
                 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0xf70f, 0x0E0E, 0x0E0F,
      /* E1x */  0xf700, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17,
                 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F,
      /* E2x */  0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
                 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F };

    // It is the responsibility of the code below to use the appropriate 
    // indexes in the arrays above. 
    
    int baseIndex = Integer.MIN_VALUE;
    int baseType = OT;
    int baseUsv = -1;
    boolean hasVowelAbove = false;
    
    while (start < limit) {
      int usv = run.elementAt (start);
      if (usv < 0x0E00 || 0x0E4F < usv) {
        baseIndex = start;
        baseType = OT;
        baseUsv = usv; 
        hasVowelAbove = false; }
      
      else {
        int thisCharClass = charClass [usv - 0x0E00];
        
        if (thisCharClass <= BASE) {
          baseIndex = start;
          baseType = thisCharClass; 
          baseUsv = usv;
          hasVowelAbove = false; }
      
        else if (thisCharClass == VB) {
          if (baseType == CR) {
            run.replace (baseIndex, removePieceUnder [baseUsv - 0x0E00]);
            baseType = C0; }
          else if (baseType == CD) {
            run.replace (start, shiftDown [usv - 0x0E30]); }}
      
        else if (thisCharClass == VA) {
          if (baseType == CA) {
            run.replace (start, shiftLeft [usv - 0x0E30]); }
          hasVowelAbove = true; }
        
        else if (thisCharClass == DA) {
          if (baseType == CA) {
            if (hasVowelAbove) {
              run.replace (start, shiftLeft [usv - 0x0E30]); }
            else {
              run.replace (start, shiftLeftAndDown [usv - 0x0E30]); }}
          else {
            if (! hasVowelAbove) {
              run.replace (start, shiftDown [usv - 0x0E30]); }}}}
      
      start++; }
      
  } 
  
  protected boolean canFormatTT() {
	  return true;
  }
  
  protected int formatTT (OpenTypeFont otFont, AttributedRun run, int start, int limit, boolean shouldKern)
      throws InvalidFontException, UnsupportedFontException, FontLoadingException {
    
    shapeTT (run, start, limit);
    return super.formatTT (otFont, run, start, limit, shouldKern);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy