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

com.adobe.xfa.text.AFEAttrSet Maven / Gradle / Ivy

There is a newer version: 2024.11.18598.20241113T125352Z-241000
Show newest version
package com.adobe.xfa.text;

import java.util.List;

import com.adobe.agl.util.ULocale;
import com.adobe.fontengine.font.Font;
import com.adobe.fontengine.inlineformatting.css20.CSS20Attribute;
import com.adobe.fontengine.inlineformatting.ElementAttribute;
import com.adobe.fontengine.inlineformatting.InterElementAttribute;
import com.adobe.fontengine.inlineformatting.LigatureLevel;

import com.adobe.xfa.font.FontInfo;
import com.adobe.xfa.font.FontInstance;
import com.adobe.xfa.ut.Storage;

/**
 * @exclude from published api.
 */

abstract class AFEAttrSet implements Comparable {
	final static int CMP_EQUAL = 0;				// return values from nullCompare()
	final static int CMP_LT = 1;
	final static int CMP_GT = 2;
	final static int CMP_UNKNOWN = 3;

	private final AFEAttrMap mAFEAttrMap;

	AFEAttrSet (AFEAttrMap afeAttrMap) {
		mAFEAttrMap = afeAttrMap;
	}

	abstract Object getAttr (Object key);

	boolean matchAttr (AFEAttrSet compare, Object attribute) {
		Object o1 = getAttr (attribute);
		Object o2 = compare.getAttr (attribute);
		return nullCompare (o1, o2) == CMP_EQUAL;
	}

	AFEAttrMap getAFEAttrMap () {
		return mAFEAttrMap;
	}

	static final boolean isFixedAttr (Object key) {
		return (key == ElementAttribute.CSS20Attribute)
			|| (key == ElementAttribute.font)
			|| (key == ElementAttribute.pointSize)
			|| (key == ElementAttribute.bidiLevel)
			|| (key == InterElementAttribute.ligatureLevel)
			|| (key == ElementAttribute.locale);
	}

	static int nullCompare (Object o1, Object o2) {
		if (o1 == o2) {
			return CMP_EQUAL;
		}
		if (o1 == null) {
			return CMP_LT;
		}
		if (o2 == null) {
			return CMP_GT;
		}
		if (o1.equals (o2)) {
			return CMP_EQUAL;
		}
		return CMP_UNKNOWN;
	}
	
	public boolean equals(Object object) {
		if (this == object)
			return true;
		
		// This overrides Object.equals(boolean) directly, so...
		if (object == null)
			return false;
		
		if (object.getClass() != getClass())
			return false;
		
		return true;
	}
}

class AFEFixedAttr extends AFEAttrSet {
	private static class DebugInfo {
		Object mKey;
		String mName;

		DebugInfo (Object key, String name) {
			mKey = key;
			mName = name;
		}
	}

	private final static DebugInfo[] gDebugInfo = {
		new DebugInfo (ElementAttribute.CSS20Attribute, "Font"),
		new DebugInfo (ElementAttribute.locale, "Locale"),
		new DebugInfo (ElementAttribute.bidiLevel, "BIDI"),
		new DebugInfo (InterElementAttribute.ligatureLevel, "Ligature"),
	};

	private final static Integer BIDI_DEFAULT_LTR = Integer.valueOf(0);
	private final static Integer BIDI_DEFAULT_RTL = Integer.valueOf(1);

	private FontInstance mFontInstance;
	private String mLocale;
	private int mBIDILevel;
	private boolean mLigature;

	private CSS20Attribute mAFEAttrs;
	private Font mAFEFont;
	private Double mAFESize;
	private ULocale mAFELocale;
	private Integer mAFEBIDILevel;
	private Object mAFELigature;

	private double mFontSize;
	private double mFontXScale;
	private double mFontYScale;
	private double mCTXScale;
	private double mCTYScale;

	AFEFixedAttr (AFEAttrMap afeAttrMap) {
		super (afeAttrMap);
	}

	boolean populate (DispRun dispRun, int bidiLevel) {
		TextAttr textAttr = dispRun.getAttr();

		mLigature = false;
		mAFELigature = LigatureLevel.MINIMUM;
		if (dispRun.allowLigatures()) {
			mLigature = true;
			mAFELigature = LigatureLevel.COMMON;
		}

		mBIDILevel = bidiLevel;
		switch (bidiLevel) {
			case 0:		mAFEBIDILevel = BIDI_DEFAULT_LTR;	break;
			case 1:		mAFEBIDILevel = BIDI_DEFAULT_RTL;	break;
			default:	mAFEBIDILevel = Integer.valueOf(bidiLevel);
		}

		mFontInstance = textAttr.fontInstance();
		String[] familyNames = new String [1];
		familyNames[0] = mFontInstance.getTypeface();
		mFontSize = Units.toFloat (mFontInstance.getSize());
		CSS20Attribute.CSSStyleValue italic = textAttr.italic() ? CSS20Attribute.CSSStyleValue.ITALIC : CSS20Attribute.CSSStyleValue.NORMAL;
		CSS20Attribute.CSSWeightValue weight = (textAttr.weight() >= FontInfo.WEIGHT_BOLD) ? CSS20Attribute.CSSWeightValue.BOLD : CSS20Attribute.CSSWeightValue.NORMAL; 
		mAFEAttrs = new CSS20Attribute (familyNames, italic, CSS20Attribute.CSSVariantValue.NORMAL, CSS20Attribute.CSSStretchValue.NORMAL, weight, mFontSize);

		mAFEFont = mFontInstance.getAFEFont();
		mAFESize = new Double (mFontSize);

		mLocale = textAttr.actualLocale();
		mAFELocale = new ULocale (mLocale);			// TODO: cache these in text context

		try {
			mCTXScale = mAFEFont.getUnitsPerEmX();
			mCTYScale = mAFEFont.getUnitsPerEmY();
			if (mCTXScale != 0) {
				mFontXScale = mFontSize / mCTXScale;
			}
			if (mCTYScale != 0) {
				mFontYScale = mFontSize / mCTYScale;
			}
		} catch (Exception e) {
			assert (false);							// TODO: what can we do about this?
			return false;
		}

		return true;
	}

	Object getAttr (Object key) {
		if (key == ElementAttribute.CSS20Attribute) {
			return mAFEAttrs;
		} else if (key == ElementAttribute.font) {
			return mAFEFont;
		} else if (key == ElementAttribute.pointSize) {
			return mAFESize;
		} else if (key == ElementAttribute.bidiLevel) {
			return mAFEBIDILevel;
		} else if (key == InterElementAttribute.ligatureLevel) {
			return mAFELigature;
		} else if (key == ElementAttribute.locale) {
			return mAFELocale;
		}
		return null;
	}

	FontInstance getFontInstance () {
		return mFontInstance;		
	}

	double getFontSize () {
		return mFontSize;
	}

	double getFontXScale () {
		return mFontXScale;
	}

	double getFontYScale () {
		return mFontYScale;
	}

	double getCTXScale () {
		return mCTXScale;
	}

	double getCTYScale () {
		return mCTYScale;
	}

	public String toString () {
		StringBuilder result = new StringBuilder();
		for (int i = 0; i < gDebugInfo.length; i++) {
			if (result.length() > 0) {
				result.append (", ");
			}
			DebugInfo info = gDebugInfo[i];
			Object key = info.mKey;
			Object value = getAttr (key);
			if (value != null) {
				result.append (info.mName);
				result.append (": ");
				if (key == ElementAttribute.CSS20Attribute) {
					CSS20Attribute css20Attr = (CSS20Attribute) value;
					result.append ("(" + cssToString (css20Attr) + ")");
				} else {
					result.append (value.toString());
				}
			}
		}
		return result.toString();
	}

	public int compareTo (AFEAttrSet compare) {
		
		if (compare == this)
			return 0;
		
		if (compare == null)
			throw new NullPointerException();
		
		if (! (compare instanceof AFEFixedAttr)) {
			return 1;
		}
		
		AFEFixedAttr other = (AFEFixedAttr) compare;
		
		int result = mFontInstance.compareTo (other.mFontInstance);
		if (result != 0) {
			return result;
		}
		
		result = mLocale.compareTo (other.mLocale);
		if (result != 0) {
			return 0;
		}
		
		if (mBIDILevel < other.mBIDILevel) {
			return -1;
		} 
		else if (mBIDILevel < other.mBIDILevel) {
			return 1;
		}
		
		if (mLigature != other.mLigature) {
			return mLigature ? 1 : -1;
		}
		
		return 0;
	}
	
	public boolean equals(Object object) {
		if (this == object)
			return true;
		
		if (!super.equals(object))
			return false;
		
		AFEFixedAttr other = (AFEFixedAttr)object;
		
		if (!mFontInstance.equals(other.mFontInstance))
			return false;
		
		if (!mLocale.equals(other.mLocale))
			return false;
		
		if (mBIDILevel != other.mBIDILevel)
			return false;
		
		if (mLigature != other.mLigature)
			return false;
		
		return true;
	}
	
	public int hashCode() {
		int result = mFontInstance.hashCode();
		result = (result * 31) ^ mLocale.hashCode();
		result = (result * 31) ^ mBIDILevel;
		result = (result * 31) ^ Boolean.valueOf(mLigature).hashCode();
		return result;
	}

	@SuppressWarnings("unchecked")
	private static String cssToString (CSS20Attribute attr) { 
		String result;
		List names = attr.getFamilyNamesList();	// unchecked warning
		result = "Face: " + names.get(0);
		result = result + ", Italic: " + ((attr.getStyle() == CSS20Attribute.CSSStyleValue.ITALIC) ? "yes" : "no");
		result = result + ", Weight: " + ((attr.getWeight() >= FontInfo.WEIGHT_BOLD) ? "bold" : "normal");
		result = result + ", Size: " + attr.getPointSize();
		return result;
	}
}

class AFEVarAttr extends AFEAttrSet {
	private final static int INITIAL_ATTR_SIZE = 8;		// TODO: may want a way to track usage

	private Storage mAttrs;

	AFEVarAttr (AFEAttrMap afeAttrMap) {
		super (afeAttrMap);
	}

	AFEVarAttr (AFEVarAttr source) {
		super (source.getAFEAttrMap());
		if (source.mAttrs != null) {
			mAttrs = new Storage (source.mAttrs);
		}
	}

	public int compareTo (AFEAttrSet compare) {
		
		if (this == compare)
			return 0;
		
		if (compare == null)
			throw new NullPointerException();
		
		if (!(compare instanceof AFEVarAttr))
			return -1;
		
		AFEVarAttr attrSet = (AFEVarAttr) compare;
		
		final int attrCount = getAFEAttrMap().getAFEIndexCount();
		for (int i = 0; i < attrCount; i++) {
			Object value1 = getAttr(i);
			Object value2 = attrSet.getAttr(i);
			switch (nullCompare (value1, value2)) {
			
				case CMP_LT:
					return -1;
					
				case CMP_GT:
					return 1;
					
				case CMP_UNKNOWN:
					int h1 = value1.hashCode();
					int h2 = value2.hashCode();
					
					if (h1 < h2)
						return -1;
					
					if (h1 > h2)
						return 1;
					
					int cmp = value1.toString().compareTo (value2.toString());
					if (cmp != 0)
						return cmp;
			}
		}
		
		return 0;
	}
	
	public boolean equals(Object object) {
		
		if (this == object)
			return true;
		
		if (!super.equals(object))
			return false;
		
		AFEVarAttr other = (AFEVarAttr)object;
		
		final int attrCount = getAFEAttrMap().getAFEIndexCount();
		for (int i = 0; i < attrCount; i++) {
			Object value1 = getAttr(i);
			Object value2 = other.getAttr(i);
			switch (nullCompare (value1, value2)) {
			
				case CMP_LT:
				case CMP_GT:
					return false;
					
				case CMP_UNKNOWN:
					int h1 = value1.hashCode();
					int h2 = value2.hashCode();
					
					if (h1 != h2)
						return false;
					
					if (!value1.toString().equals(value2.toString()))
						return false;
			}
		}
		
		return true;
	}
	
	public int hashCode() {
		int hash = 13;
		final int attrCount = getAFEAttrMap().getAFEIndexCount();
		hash = (hash * 31) ^ attrCount;
		for (int i = 0; i < attrCount; i++) {
			Object value = getAttr(i);
			hash = (hash * 31) ^ (value == null ? 0 : value.hashCode());
		}
		
		return hash;
	}

	Object getAttr (Object key) {
		return getAttr (getAFEAttrMap().mapAFEIndex (key));
	}

	Object getAttr (int index) {
		return ((mAttrs != null) && (index < mAttrs.size())) ? mAttrs.get (index) : null;
	}

	boolean matchAttr (AFEAttrSet compare, Object attribute) {
		assert (compare instanceof AFEVarAttr);
		AFEVarAttr other = (AFEVarAttr) compare;
		int index = getAFEAttrMap().mapAFEIndex (attribute);
		Object o1 = getAttr (index);
		Object o2 = other.getAttr (index);
		return nullCompare (o1, o2) == CMP_EQUAL;
	}

	void setAttr (Object key, Object value) {
		setAttr (getAFEAttrMap().mapAFEIndex (key), value);
	}

	void setAttr (int index, Object value) {
		if (mAttrs == null) {
			mAttrs = new Storage (INITIAL_ATTR_SIZE);
		}
		if (index >= mAttrs.size()) {
			mAttrs.setSize (index + 1);
		}
		mAttrs.set (index, value);
	}

	public String toString () {
		StringBuilder result = new StringBuilder();
		Object[] afeKeys = getAFEAttrMap().getAFEKeys();
		int attrCount = mAttrs.size();
		assert (attrCount <= afeKeys.length);
		for (int i = 0; i < attrCount; i++) {
			Object value = mAttrs.get (i);
			if (value != null) {
				if (result.length() > 0) {
					result.append (", ");
				}
				result.append (afeKeys[i].toString());
				result.append (": ");
				result.append (value.toString());
			}
		}
		return result.toString();
	}
}

class AFEVarAttrTest extends AFEVarAttr {
	private AFEVarAttr mBase;
	private int mIndex;
	private Object mValue;

	AFEVarAttrTest (AFEAttrMap afeAttrMap) {
		super (afeAttrMap);
	}
	
	public boolean equals(Object object) { // NOPMD - UselessOverridingMethod - here for documentation/FindBugs suppression
		
		// The fields added in this derived class do not participate in equality
		return super.equals(object);
	}
	
	public int hashCode() { // NOPMD - UselessOverridingMethod - here for documentation/FindBugs suppression
		// The fields added in this derived class do not participate in equality
		return super.hashCode();
	}

	AFEVarAttr create () {
		AFEVarAttr result = new AFEVarAttr (mBase);
		result.setAttr (mIndex, mValue);
		return result;
	}

	Object getAttr (int index) {
		return (index == mIndex) ? mValue : mBase.getAttr(index);
	}

	void setup (AFEVarAttr base, Object key, Object value) {
		mBase = base;
		mIndex = getAFEAttrMap().mapAFEIndex (key);
		mValue = value;
	}
}