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

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

/*
*
*	File: ArabicFormatter.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.agl.lang.UCharacter;
import com.adobe.agl.lang.UProperty;
import com.adobe.agl.util.ULocale;
import com.adobe.fontengine.CharUtil;
import com.adobe.fontengine.font.Font;
import com.adobe.fontengine.font.FontImpl;
import com.adobe.fontengine.font.FontLoadingException;
import com.adobe.fontengine.font.InvalidFontException;
import com.adobe.fontengine.font.UnsupportedFontException;
import com.adobe.fontengine.font.opentype.OTSelector;
import com.adobe.fontengine.font.opentype.OTSelectorOnElementAttribute;
import com.adobe.fontengine.font.opentype.OTSelectors;
import com.adobe.fontengine.font.opentype.OpenTypeFont;
import com.adobe.fontengine.font.opentype.Tag;
import com.adobe.fontengine.inlineformatting.AttributedRun;
import com.adobe.fontengine.inlineformatting.ElementAttribute;
import com.adobe.fontengine.inlineformatting.InterElementAttribute;
import com.adobe.fontengine.inlineformatting.LigatureLevel;

final class ArabicFormatter extends BaseFormatter {
  
  //------------------------------------------------------------ first pass ---
  
  /** Enumeration of the possible joining shapes.
   */
  private static class JoiningShapes {
    private String name;
    
    private JoiningShapes (String name) {
      this.name = name;
    }
    
    public String toString () {
      return name; 
    }
    
    public static final JoiningShapes init = new JoiningShapes ("init");
    public static final JoiningShapes isol = new JoiningShapes ("isol");
    public static final JoiningShapes medi = new JoiningShapes ("medi");
    public static final JoiningShapes fina = new JoiningShapes ("fina");
  }

  /** Attribute to carry the joining shape.
   * Its value must be a JoiningShape.
   */
  private static final ElementAttribute joiningShape
    = new ElementAttribute ("joiningShape");
  
  
  /** Set the joiningShape attribute on a run.
   * The joiningShape attribute is the actual shape that non transparent 
   * characters must take. This code assumes that the subrun being processed
   * is bracketed by NON_JOINING characters. 
   * The elements between start and limit must be at the same directionality
   * level.
   */
  protected void setJoiningShape (AttributedRun run, int start, int limit) {
     
    int previousIndex = Integer.MIN_VALUE;
    boolean previousJoinsItsRight = false;
    int previousType = UCharacter.JoiningType.NON_JOINING;
        
    for (int i = start; i < limit + 1; i++) {
      int thisType;
      if (i == limit) {
        thisType = UCharacter.JoiningType.NON_JOINING; }
      else if (run.getElementStyle (i, ElementAttribute.isGlyph) != Boolean.TRUE) {
        thisType = UCharacter.getIntPropertyValue (run.elementAt (i), UProperty.JOINING_TYPE); }
      else {
        thisType = ((Integer) run.getElementStyle (i, ElementAttribute.joiningType)).intValue (); }
      
      if (thisType == UCharacter.JoiningType.TRANSPARENT) {
        continue; }
      
      JoiningShapes previousShape;
      if (   (   thisType == UCharacter.JoiningType.JOIN_CAUSING 
              || thisType == UCharacter.JoiningType.DUAL_JOINING 
              || thisType == UCharacter.JoiningType.RIGHT_JOINING)
          && (   previousType == UCharacter.JoiningType.JOIN_CAUSING 
              || previousType == UCharacter.JoiningType.DUAL_JOINING
              || previousType == UCharacter.JoiningType.LEFT_JOINING)) {
        
        previousShape = previousJoinsItsRight ? JoiningShapes.medi : JoiningShapes.init;
        previousJoinsItsRight = true; }
      else {
        previousShape = previousJoinsItsRight ? JoiningShapes.fina : JoiningShapes.isol;
        previousJoinsItsRight = false; }
      
      if (previousIndex != Integer.MIN_VALUE 
          && previousType != UCharacter.JoiningType.JOIN_CAUSING
          && previousType != UCharacter.JoiningType.NON_JOINING) {
        run.setElementStyle (previousIndex, joiningShape, previousShape); }
      
      previousIndex = i;
      previousType = thisType; }
  }
    
  public int firstPass (AttributedRun run, int start, int limit) {
    
    // setJoiningShape needs to see elements at the same bidi level.
    while (start < limit) {
      int bidiRunLimit = run.getSubrunLimit (start, limit, ElementAttribute.bidiLevel);
      setJoiningShape (run, start, bidiRunLimit);
      start = bidiRunLimit; }
      
    return super.firstPass (run, start, limit);
  }
  
  //----------------------------------------------------- canRenderWithFont ---
//  public int canRenderWithFont (Font font, AttributedRun run, int start, int limit, boolean notdefOK)
//      throws InvalidFontException, UnsupportedFontException {
//    FontData fontData = ((FontImpl) font).getFontData();
//    
//    if (fontData instanceof OpenTypeFont) {
//      return super.canRenderWithFont (font, run, start, limit, notdefOK); }
//      
//    return 0;
//  }
  
  //------------------------------------------------------- OpenType layout ---
  protected int setGlyphs (AttributedRun run, int start, int limit) 
      throws InvalidFontException, UnsupportedFontException, FontLoadingException {
    
    Font font = (Font) run.getElementStyle (start, ElementAttribute.font);
    OpenTypeFont otFont = (OpenTypeFont) ((FontImpl) font).getFontData();
    
    while (start < limit) {
      if (run. getElementStyle (start, ElementAttribute.isGlyph) == Boolean.TRUE) {
        start++;
        continue; }
      
      int usv = run.elementAt (start);
      
      if (usv == 0x200D /* ZWJ */ || usv == 0x200C /* ZWNJ */) {
        run.remove (start);
        limit--; 
        run.setInterElementStyleBefore (start, 
            InterElementAttribute.ligatureLevel, 
            LigatureLevel.NONE);
        continue; }
      
      if (CharUtil.isControl (usv)) {
        run.remove (start);
        limit--; 
        continue; }
      
      if (((Integer)run.getElementStyle (start, ElementAttribute.bidiLevel)).intValue () % 2 == 1) {      
        usv = UCharacter.getMirror (usv); }
      
      if (   ! CharUtil.isBase (usv) 
          || start + 1 == limit
          || run.getElementStyle (start + 1, ElementAttribute.isGlyph) == Boolean.TRUE
          || ! CharUtil.isCombining (run.elementAt (start + 1))) {
        // common case: a single character
        int gid = otFont.getGlyphForChar (usv);
        run.replace (start, gid);
        run.setElementStyle (start, ElementAttribute.isGlyph, Boolean.TRUE);
        start++; 
        continue; }
      
      { int graphemeLimit = start + 1;
      while (   graphemeLimit < limit 
          && run.getElementStyle (graphemeLimit, ElementAttribute.isGlyph) != Boolean.TRUE
          && CharUtil.isCombining (run.elementAt (graphemeLimit))) {
        graphemeLimit++; }
      
      boolean directMappingHasNotdef = false;
      int[] usvs = new int [graphemeLimit - start];
      int[] gids = new int [graphemeLimit - start];
      
      for (int i = start; i < graphemeLimit; i++) {
        usvs [i - start] = run.elementAt (i);
        int gid = otFont.getGlyphForChar (usvs [i - start]);
        if (gid == 0) {
          directMappingHasNotdef = true; }
        gids [i - start] = gid; }
      
      if (directMappingHasNotdef) {
        int usvc = CharUtil.compose (usvs, 0, graphemeLimit - start);
        if (usvc != -1) {
          int gid = otFont.getGlyphForChar (usvc);
          if (gid != 0) {
            run.replace (start, graphemeLimit, gid);
            run.setElementStyle (start, ElementAttribute.isGlyph, Boolean.TRUE);
            limit -= (graphemeLimit - start - 1); 
            start++; 
            continue; }}}
      
      for (int i = start; i < graphemeLimit; i++) {
        run.replace (i, gids [i - start]);
        run.setElementStyle (start, ElementAttribute.isGlyph, Boolean.TRUE); }
      start = graphemeLimit; }}
    
    return limit;
  }
  

  //-------------------------------------------------------------- formatOT ---
  protected boolean canFormatOT() {
	  return true;
  }
  
  protected int formatOT (OpenTypeFont otFont, AttributedRun run, int start, int limit, boolean shouldKern)
      throws InvalidFontException, UnsupportedFontException, FontLoadingException {
    // PRE: r.font [first, last] = one font
    //      r.direction [first, last] = one direction
    //      charRun is not defective, i.e. does not start in the 
    //      middle of a default grapheme cluster
    
   
    Integer bidiLevel = (Integer) run.getElementStyle (start, ElementAttribute.bidiLevel);
    int scriptTag = getOTScriptTag ((Integer) run.getElementStyle (start, InFontFormatter.scriptAttribute));
    int langTag = getOTLanguageTag ((ULocale) run.getElementStyle (start, ElementAttribute.locale));
    
    // Map characters to glyphs; simply skip existing glyphs
    limit = setGlyphs (run, start, limit);
    
    if (otFont.gsub != null) {
      // Shape the glyphs
      int[][] gsubLookups = LookupsCache.resolveFeatureTag (otFont.gsub, scriptTag, langTag, gsubFeatures);
      
      limit = otFont.gsub.applyLookups (gsubLookups [0], run, start, limit, OTSelectors.everywhere, otFont.gdef); // ccmp
      limit = otFont.gsub.applyLookups (gsubLookups [1], run, start, limit, OTSelectors.everywhere, otFont.gdef); // locl
      limit = otFont.gsub.applyLookups (gsubLookups [2], run, start, limit, initSelector, otFont.gdef); // init
      limit = otFont.gsub.applyLookups (gsubLookups [3], run, start, limit, mediSelector, otFont.gdef); // medi
      limit = otFont.gsub.applyLookups (gsubLookups [4], run, start, limit, finaSelector, otFont.gdef); // fina
      limit = otFont.gsub.applyLookups (gsubLookups [5], run, start, limit, isolSelector, otFont.gdef); // isol
      if (bidiLevel.intValue () % 2 == 1) {
        limit = otFont.gsub.applyLookups (gsubLookups [6], run, start, limit, OTSelectors.everywhere, otFont.gdef); }   // rtla
      limit = otFont.gsub.applyLookups (gsubLookups [7], run, start, limit, OTSelectors.minimumLigatures, otFont.gdef);  // rlig  
      limit = otFont.gsub.applyLookups (gsubLookups [8], run, start, limit, OTSelectors.commonLigatures, otFont.gdef); }  // liga
      
    // Position the glyphs
    posFromAdvanceWidth (run, otFont, start, limit);
    
    if (otFont.gpos != null) {
      int[][] gposLookups = LookupsCache.resolveFeatureTag (otFont.gpos, scriptTag, langTag, gposFeatures);
      if (shouldKern)
      {
    	  if (gposLookups[0].length != 0)
    	  {
    		  limit = otFont.gpos.applyLookups (gposLookups [0], run, start, limit, OTSelectors.everywhere, otFont.gdef); // kern
    	  } else {
    		  applyKernTable(otFont, run, start, limit);
    	  }
      }
      limit = otFont.gpos.applyLookups (gposLookups [1], run, start, limit, OTSelectors.everywhere, otFont.gdef); // mark
      limit = otFont.gpos.applyLookups (gposLookups [2], run, start, limit, OTSelectors.everywhere, otFont.gdef); } // mkmk
    else {
    	applyKernTable(otFont, run, start, limit);
    }
    return limit;
  }
    
  private static final OTSelector initSelector = new OTSelectorOnElementAttribute (ArabicFormatter.joiningShape, ArabicFormatter.JoiningShapes.init, true);
  private static final OTSelector mediSelector = new OTSelectorOnElementAttribute (ArabicFormatter.joiningShape, ArabicFormatter.JoiningShapes.medi, true);
  private static final OTSelector finaSelector = new OTSelectorOnElementAttribute (ArabicFormatter.joiningShape, ArabicFormatter.JoiningShapes.fina, true);
  private static final OTSelector isolSelector = new OTSelectorOnElementAttribute (ArabicFormatter.joiningShape, ArabicFormatter.JoiningShapes.isol, true);

  private static final int[] gsubFeatures = {
      Tag.feature_ccmp, //0
      Tag.feature_locl, //1
      Tag.feature_init, //2
      Tag.feature_medi, //3
      Tag.feature_fina, //4 
      Tag.feature_isol, //5
      Tag.feature_rtla, //6
      Tag.feature_rlig, //7
      Tag.feature_liga}; //8
  
  private static final int[] gposFeatures = {
      Tag.feature_kern,
      Tag.feature_mark, 
      Tag.feature_mkmk};
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy