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

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

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

import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import com.adobe.fontengine.inlineformatting.AttributedRun;
import com.adobe.fontengine.inlineformatting.ElementAttribute;
import com.adobe.fontengine.inlineformatting.InterElementAttribute;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.Storage;

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

class AFERun implements AttributedRun {
	private Storage mElements;
	@FindBugsSuppress(code="UwF") // will never be written as long as gEnableDebugging == false
	private TextContext mContext;
	private int mInstance;
	private int mEvent;
	private boolean mMapRequired = false;
	private static final AtomicInteger gNextDebugInstance = new AtomicInteger();
	private static final boolean gEnableDebugging = false;

	AFERun (TextContext context) {
		if (gEnableDebugging && (context != null) && context.debug()) {
			mContext = context;
		}
	}

	void load (DispLineRaw line) {
		StringBuilder debugMsg = null;
		if (mContext != null) {
			mInstance = gNextDebugInstance.incrementAndGet();
			debugMsg = startDebug ("create");
			debugMsg.append ("[");
		}

		int charCount = line.getCharCount();
		if (mElements == null) {
			mElements = new Storage(charCount);
		} else {
			mElements.setSize (0);							// TODO: could try to reuse elements
		}

		int runIndex = 0;
		int runCharLimit = 0;
		AFEElement prevElement = null;
		DispRun dispRun = null;
		DispRun prevRun = null;

		MappingManager mappingManager = line.getFormatInfo().getMappingManager();
		TextContext context = line.display().getContext();

		for (int i = 0; i < charCount; i++) {
			if ((debugMsg != null) && (i > 0)) {
				debugMsg.append (", ");
			}

			int bidiLevel = 0;
			if (mappingManager != null) {
				bidiLevel = mappingManager.getLevel (i);
			}

			DispEmbed embed = line.isObject (i);
			if (embed != null) {
				AFEElement element = new AFEElement (context, embed.getEmbed(), i);
				element.populateEmbed (dispRun, bidiLevel);
				mElements.add (element);
				prevRun = null;
				prevElement = null;

				if (debugMsg != null) {
					debugMsg.append (" object (");
					debugMsg.append (element.getEmbed().toString());
					debugMsg.append (")");
				}

			} else {
				if (i >= runCharLimit) {
					dispRun = line.getRun (runIndex);
					runCharLimit = dispRun.getMapIndex() + dispRun.getMapLength();
					runIndex++;
				}
	
				int c = line.getChar (i);
				AFEElement element = new AFEElement (context, c, i);
				element.populateChar (prevElement, dispRun, prevRun, bidiLevel);
				mElements.add (element);
				prevElement = element;
				prevRun = dispRun;
	
				if (debugMsg != null) {
					if (c == '\'') {
						debugMsg.append ("'\\''");
					} else if ((c >= 0x20) && (c < 0x7F)) {
						debugMsg.append ('\'');
						debugMsg.append ((char) c);
						debugMsg.append ('\'');
					} else {
						debugMsg.append (Units.hexToString (c));
					}
				}
			}
		}

		if (debugMsg != null) {
			debugMsg.append (']');
			mContext.debug (debugMsg.toString());
		}
	}

	int getSize () {
		return mElements.size();
	}

	AFEElement getElement (int position) {
		return e (position);
	}

	boolean isMapRequired () {
		return mMapRequired;
	}

	public void adjustPlacementAndAdvance (int position, double xPlacementDelta, double yPlacementDelta, double xAdvanceDelta, double yAdvanceDelta) {
		AFEElement element = null;
		if ((xPlacementDelta != 0) || (yPlacementDelta != 0) || (xAdvanceDelta != 0) || (yAdvanceDelta != 0)) {
			element = e (position);
			element.adjustPlacementAndAdvance (xPlacementDelta, yPlacementDelta, xAdvanceDelta, yAdvanceDelta);
			debugElementPlacementAndAdvance (element, position, "adjustPlacementAndAdvance");
		}
	}

	public int elementAt (int position) {
		return e(position).elementAt();
	}

	public Object getElementStyle (int position, ElementAttribute attribute) {
		return e(position).getElementStyle (attribute);
	}

	public double getElementXAdvance (int position) {
		return e(position).getElementXAdvance();
	}

	public double getElementXPlacement( int position) {
		return e(position).getElementXPlacement();
	}

	public double getElementYAdvance (int position) {
		return e(position).getElementYAdvance();
	}

	public double getElementYPlacement (int position) {
		return e(position).getElementYPlacement();
	}

	public Object getInterElementStyleBefore (int position, InterElementAttribute attribute) {
		return e(position).getInterElementStyleBefore (attribute);
	}

	public int getSubrunLimit (int start, int limit, ElementAttribute attribute) {
		AFEElement first = e (start);
		for (int test = start+1; test < limit; test++) {
			if (! first.matchAttr (e (test), attribute)) {
				return test;
			}
		}
		return limit;
	}

	public int getSubrunLimit (int start, int limit, @SuppressWarnings("unchecked") Set attributes) {
		AFEElement first = e (start);
		for (int test = start+1; test < limit; test++) {
			if (! first.matchAttr (e (test), attributes)) {
				return test;
			}
		}
		return limit;
	}

	public void remove (int position) {
		mElements.remove (position);
		mMapRequired = true;
	}

	public void replace (int[] positions, int[] elementIDs) {
		replaceManyWithMany (positions, elementIDs);
	}

	public void replace (int position, int elementID) {
		replaceOneWithOne (position, elementID);
	}

	public void replace (int position, int[] elementIDs) {
		replaceOneWithMany (position, elementIDs);
	}

	public void replace (int[] positions, int elementID) {
		replaceManyWithOne (positions, elementID);
	}

	public void replace (int start, int limit, int elementID) {
		replaceRangeWithOne (start, limit, elementID);
	}

	public void setElementPlacementAndAdvance (int position, double xPlacement, double yPlacement, double xAdvance, double yAdvance) {
		AFEElement element = e (position);
		element.setElementPlacementAndAdvance (xPlacement, yPlacement, xAdvance, yAdvance);
		debugElementPlacementAndAdvance (element, position, "setElementPlacementAndAdvance");
	}

	public void setElementStyle (int position, ElementAttribute attribute, Object value) {
		debugElementStyle (position, position+1, attribute, value);
		e(position).setElementStyle (attribute, value);
	}

	public void setElementStyle (int start, int limit, ElementAttribute attribute, Object value) {
		debugElementStyle (start, limit, attribute, value);
		for (int i = start; i < limit; i++) {
			e(i).setElementStyle (attribute, value);
		}
	}

	public void setInterElementStyleBefore (int position, InterElementAttribute attribute, Object value) {
		debugInterElementStyle (position, position+1, attribute, value);
		e(position).setInterElementStyleBefore (attribute, value);
	}

	public void setInterElementStyleBefore (int start, int limit, InterElementAttribute attribute, Object value) {
		debugInterElementStyle (start, limit, attribute, value);
		for (int i = start; i < limit; i++) {
			e(i).setInterElementStyleBefore (attribute, value);
		}
	}

	public void startWorkingWithPositions (int start, int limit) {
		if (mContext != null) {
			StringBuilder debugMsg = startDebug ("startWorkingWithPositions");
			debugMsg.append ('[');
			debugInt (debugMsg, start);
			debugMsg.append ('-');
			debugInt (debugMsg, limit - 1);
			debugMsg.append (']');
			mContext.debug (debugMsg.toString());
		}
		for (int i = start; i < limit; i++) {
			e(i).startWorkingWithPositions();
		}
	}

	public String toString () {
		StringBuilder result = new StringBuilder("Run:\n");
		for (int i = 0; i < mElements.size(); i++) {
			result.append("  (");
			result.append(e(i).toString());
			result.append(")\n");
		}
		return result.toString();
	}

	private AFEElement e (int position) {
		assert (position < mElements.size());
		return mElements.get(position);
	}

	private void insert (int position, int[] elementIDs) {
		AFEElement sample = e (position);
		sample.setElement (elementIDs[0], true);
		sample.setInMultiple (true);
		for (int i = 1; i < elementIDs.length; i++) {
			AFEElement insert = new AFEElement (sample);
			insert.setElement (elementIDs[i], true);
			mElements.add (position, insert);
			position++;
		}
		mMapRequired = true;
	}

	private void remove (int[] positions) {
		for (int i = positions.length; i > 1; i--) {	// don't remove first
			i--;
			mElements.remove (positions[i]);
		}
		mMapRequired = true;
	}

	private AFEElement removeRange (int start, int limit) {
		for (limit--; limit > start; limit--) {
			mElements.remove (limit);
		}
		mMapRequired = true;
		return e (start);								// not removed
	}

	private void replaceManyWithMany (int[] positions, int[] elementIDs) {
		assert (positions.length > 0);
		int i;
		if (elementIDs.length == 0) {
			for (i = positions.length; i > 0; ) {
				i--;
				remove (positions[i]);
			}
		} else if (elementIDs.length == 1) {
			replaceManyWithOne (positions, elementIDs[0]);
		} else if (positions.length == 1) {
			replaceOneWithMany (positions[0], elementIDs);
		} else if (isSequential (positions)) {
			replaceRangeWithMany (positions[0], positions[0] + positions.length, elementIDs);
		} else {
			mMapRequired = true;
			debugReplace (positions, elementIDs);
			remove (positions);
			insert (positions[0], elementIDs);			// TODO: no way to map this in current glyph loc architecture
		}
	}

	private void replaceManyWithOne (int[] positions, int elementID) {
		assert (positions.length > 0);
		if (positions.length == 1) {
			replaceOneWithOne (positions[0], elementID);
		} else if (isSequential (positions)) {
			replaceRangeWithOne (positions[0], positions[0] + positions.length, elementID);
		} else {
			assert (false);								// TODO: not supported in AXTE mapping
			if (mContext != null) {
				int[] elementIDs = new int [1];
				elementIDs[0] = elementID;
				debugReplace (positions, elementIDs);
			}
			remove (positions);
			AFEElement afeElement = e (positions[0]);
			afeElement.setElement (elementID, true);
			afeElement.setMapLength (afeElement.getMapLength() + positions.length - 1);	// TODO: need to rework glyph loc map for this to make sense
		}
	}

	private void replaceOneWithMany (int position, int[] elementIDs) {
		if (elementIDs.length == 0) {
			remove (position);
		} else if (elementIDs.length == 1) {
			replaceOneWithOne (position, elementIDs[0]);
		} else {
			if (mContext != null) {
				int[] positions = new int [1];
				positions[0] = position;
				debugReplace (positions, elementIDs);
			}
			insert (position, elementIDs);
		}
	}

	private void replaceOneWithOne (int position, int elementID) {
		if (mContext != null) {
			int[] positions = new int [] {position};
			int[] elementIDs = new int [] {elementID};
			debugReplace (positions, elementIDs);
		}

		e(position).setElement (elementID, false);
	}

	private void replaceRangeWithOne (int start, int limit, int elementID) {
		if (mContext != null) {
			int[] elementIDs = new int [] {elementID};
			debugReplaceRange (start, limit, elementIDs);
		}
		AFEElement keeper = removeRange (start, limit);
		keeper.setElement (elementID, true);
		keeper.setMapLength (keeper.getMapLength() + (limit - start - 1));
	}

	private void replaceRangeWithMany (int start, int limit, int[] elementIDs) {
		debugReplaceRange (start, limit, elementIDs);
		removeRange (start, limit);
		AFEElement keeper = removeRange (start, limit);
		keeper.setMapLength (keeper.getMapLength() + (limit - start - 1));
		insert (start, elementIDs);
	}

	private static boolean isSequential (int[] positions) {
		assert (positions.length > 0);
		int prev = positions[0];
		for (int i = 1; i < positions.length; i++) {
			prev++;
			if (positions[i] != prev) {
				return false;
			}
		}
		return true;
	}

	private StringBuilder startDebug (String event) {
		StringBuilder result = new StringBuilder();
		result.append (Units.intToString (mInstance, 4));
		result.append ("(");
		result.append (Units.intToString (++mEvent, true));
		result.append (") ");
		result.append (event);
		result.append (": ");
		return result;
	}

	public void debugElementPlacementAndAdvance (AFEElement element, int position, String event) {
		if (mContext == null) {
			return;
		}
		StringBuilder debugMsg = startDebug (event);
		debugMsg.append ("[");
		debugInt (debugMsg, position);
		debugMsg.append ("] placement (");
		debugMsg.append (Double.toString (element.getScaledXPlacement()));
		debugMsg.append (", ");
		debugMsg.append (Double.toString (element.getScaledYPlacement()));
		debugMsg.append (") advance (");
		debugMsg.append (Double.toString (element.getScaledXAdvance()));
		debugMsg.append (", ");
		debugMsg.append (Double.toString (element.getScaledYAdvance()));
		debugMsg.append (")");
		mContext.debug (debugMsg.toString());
	}

	private final void debugReplace (int[] positions, int[] elementIDs) {
		if (mContext == null) {
			return;
		}
		int i;
		StringBuilder debugMsg = startDebug ("replace");
		debugMsg.append ('[');
		int count = 0;
		int start = -1;
		int prev = Integer.MAX_VALUE;
		for (i = 0; i < positions.length; i++) {
			int pos = positions[i];
			if (pos != (prev + 1)) {
				if (debugReplaceRange (debugMsg, count, start, prev)) {
					count++;
					start = pos;
				}
			}
			prev = pos;
			debugInt (debugMsg, i, positions[i]);
		}
		debugReplaceRange (debugMsg, count, start, prev);
		debugMsg.append ("] [");
		for (i = 0; i < elementIDs.length; i++) {
			debugHex (debugMsg, i, elementIDs[i]);
		}
		debugMsg.append (']');
		mContext.debug (debugMsg.toString());
	}

	private void debugReplaceRange (int start, int limit, int[] elementIDs) {
		if (mContext == null) {
			return;
		}
		int length = limit - start;
		int[] positions = new int [length];
		for (int i = 0; i < length; i++) {
			positions[i] = start + i;
		}
		debugReplace (positions, elementIDs);
	}

	private final boolean debugReplaceRange (StringBuilder debugMsg, int count, int start, int end) {
		if (start < 0) {
			return false;
		}
		StringBuilder range = new StringBuilder();
		debugInt (range, start);
		if (end > start) {
			range.append ('-');
			debugInt (range, end);
		}
		debugElement (debugMsg, count, range.toString());
		return true;
	}

	private final void debugElementStyle (int start, int limit, ElementAttribute attribute, Object value) {
		debugStyle ("setElementStyle", start, limit, attribute, value);
	}

	private final void debugInterElementStyle (int start, int limit, InterElementAttribute attribute, Object value) {
		debugStyle ("setInterElementStyle", start, limit, attribute, value);
	}

	private final void debugStyle (String type, int start, int limit, Object attribute, Object value) {
		if (mContext == null) {
			return;
		}
		StringBuilder debugMsg = startDebug (type);
		debugMsg.append ("[");
		debugInt (debugMsg, start);
		if ((limit - start) > 1) {
			debugMsg.append ("-");
			debugInt (debugMsg, limit - 1);
		}
		debugMsg.append ("] attribute (");
		debugMsg.append (attribute.toString());
		debugMsg.append (") value (");
		debugMsg.append (value.toString());
		debugMsg.append (")");
		mContext.debug (debugMsg.toString());
	}

	private final static void debugHex (StringBuilder result, int index, int value) {
		debugElement (result, index, Units.hexToString (value, 4, "0x"));
	}

	private final static void debugInt (StringBuilder result, int value) {
		debugInt (result, 0, value);
	}

	private final static void debugInt (StringBuilder result, int index, int value) {
		debugElement (result, index, Units.intToString (value, 0));
	}

	private final static void debugElement (StringBuilder result, int index, String value) {
		if (index > 0) {
			result.append (", ");
		}
		result.append (value);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy