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

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

There is a newer version: 2024.9.17689.20240905T073330Z-240800
Show newest version
package com.adobe.xfa.text;

import java.util.Map;
import com.adobe.xfa.font.FontInstance;
import com.adobe.xfa.gfx.GFXDecorationInfo;
import com.adobe.xfa.gfx.GFXGlyphOrientation;
import com.adobe.xfa.ut.UnitSpan;

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

abstract class Decoration {

/**
 * @exclude from published api.
 */
	static class DecorationKey implements Comparable {
		
		final float moXStart;
		final float moXEnd;
		
		public DecorationKey(float xStart, float xEnd) {
			moXStart = xStart;
			moXEnd = xEnd;
		}

		public int compareTo(DecorationKey compare) {
			if (compare == null)
				throw new NullPointerException();
			
			if (moXStart < compare.moXStart) {
				return -1;
			}
			if (moXStart > compare.moXStart) {
				return 1;
			}
			if (moXEnd < compare.moXEnd) {
				return -1;
			}
			if (moXEnd > compare.moXEnd) {
				return 1;
			}
			
			return 0;
		}

		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 (compareTo((DecorationKey)object) == 0);
		}
		
		public int hashCode() {
			int hash = 13;
			hash = (hash * 31) ^ Float.floatToIntBits(moXStart);
			hash = (hash * 31) ^ Float.floatToIntBits(moXEnd);
			return hash;
		}
		
	}

/**
 * @exclude from published api.
 */
	static class DecorationValue {
		
		final UnitSpan moYOffset;
		final UnitSpan moWidth;
		
		public DecorationValue(UnitSpan yOffset, UnitSpan width) {
			moYOffset = yOffset;
			moWidth = width;
		}
	}

/**
 * @exclude from published api.
 */
	private static class Underline extends Decoration {
		private static final double SINGLE_OFFSET = 0.4;
		private static final double SINGLE_WIDTH = 0.16;
		private static final double DOUBLE_OFFSET_1 = 0.4;
		private static final double DOUBLE_OFFSET_2 = 0.9;
		private static final double DOUBLE_WIDTH = 0.08;

		Underline (DispLineWrapped poLine, DrawParm oParm) {
			super (poLine, oParm);
			setSpecial (SPECIAL_HONOUR_SUBSCRIPT);
			setUseDescent (true);
		}

		int getLineState (TextAttr poAttr) {
			return (poAttr == null) ? GFXDecorationInfo.DECORATE_UNKNOWN : poAttr.underline();
		}

		void onLineStateChange () {
			switch (getCount()) {
				case 1:
					setFactors (SINGLE_WIDTH, SINGLE_OFFSET);
					break;
				case 2:
					setFactors (DOUBLE_WIDTH, DOUBLE_OFFSET_1, DOUBLE_OFFSET_2);
					break;
			}
		}
	}

/**
 * @exclude from published api.
 */
	private static class LineThrough extends Decoration {
		private static final double SINGLE_OFFSET = 0.4;
		private static final double SINGLE_WIDTH = 0.04;
		private static final double DOUBLE_OFFSET_1 = 0.36;
		private static final double DOUBLE_OFFSET_2 = 0.44;
		private static final double DOUBLE_WIDTH = 0.03;

		LineThrough (DispLineWrapped poLine, DrawParm oParm) {
			super (poLine, oParm);
			setSpecial (SPECIAL_BREAK_ON_ALL_CHANGES);
		}

		int getLineState (TextAttr poAttr) {
			return (poAttr == null) ? GFXDecorationInfo.DECORATE_UNKNOWN : poAttr.strikeout();
		}

		void onLineStateChange () {
			switch (getCount()) {
				case 1:
					setFactors (SINGLE_WIDTH, SINGLE_OFFSET);
					break;
				case 2:
					setFactors (DOUBLE_WIDTH, DOUBLE_OFFSET_1, DOUBLE_OFFSET_2);
					break;
			}
		}
	}

/**
 * @exclude from published api.
 */
	private static class Overline extends Decoration {
		private static final double SINGLE_OFFSET = 1.1;
		private static final double SINGLE_WIDTH = 0.04;
		private static final double DOUBLE_OFFSET_1 = 1.1;
		private static final double DOUBLE_OFFSET_2 = 1.18;
		private static final double DOUBLE_WIDTH = 0.3;

		Overline (DispLineWrapped poLine, DrawParm oParm) {
			super (poLine, oParm);
			setSpecial (SPECIAL_HONOUR_SUPERSCRIPT);
		}

		int getLineState (TextAttr poAttr) {
			return (poAttr == null) ? GFXDecorationInfo.DECORATE_UNKNOWN : poAttr.overline();
		}

		void onLineStateChange () {
			switch (getCount()) {
				case 1:
					setFactors (SINGLE_WIDTH, SINGLE_OFFSET);
					break;
				case 2:
					setFactors (DOUBLE_WIDTH, DOUBLE_OFFSET_1, DOUBLE_OFFSET_2);
					break;
			}
		}
	}

	static final int SPECIAL_HONOUR_SUPERSCRIPT = 0;
	static final int SPECIAL_HONOUR_SUBSCRIPT = 1;
	static final int SPECIAL_BREAK_ON_ALL_CHANGES = 2;

	static final int DECORATION_SPACE = 0x01;
	static final int DECORATION_SUPPRESS = 0x02;
	static final int DECORATION_VERTICAL = 0x04;

	private int mnType;
	private int mnCount;
	private int meSpecial;
	private double mdWidth;
	private double mdOffset;
	private double mdOffset2;
	private boolean mbUseDescent;

	private UnitSpan moBaseline;
	private float moStart;
	private float moNonSpace;
	private float moSpace;
	private final LineHeight moHeight = new LineHeight();

	static Decoration createUnderline (DispLineWrapped line, DrawParm parm) {
		return new Underline (line, parm);
	}

	static Decoration createLineThrough (DispLineWrapped line, DrawParm parm) {
		return new LineThrough (line, parm);
	}

	static Decoration createOverline (DispLineWrapped line, DrawParm parm) {
		return new Overline (line, parm);
	}

	void update (DrawParm oParm, TextAttr poAttr, float oLeft, float oRight, int nFlags, Map oDecorations) {
		boolean bBreak = false;			// true if change in underlining

// Establish nType: the type of text decoration line (none, word, all)
// and nCount: the number of underlines (0, 1, 2).
		int nCount = 0;
		int nType = GFXDecorationInfo.DECORATE_NONE;
		if (poAttr != null) {
			GFXDecorationInfo info = GFXDecorationInfo.extractDecoration (getLineState (poAttr));
			nCount = info.mCount;
			nType = info.mType;
		}
		boolean bIsSpace = (nFlags & DECORATION_SPACE) != 0;

// Now, apply various adjustments to the line type and count based on
// this item.

// Note: code changed to use pointers rather than copying font instance
// objects, since the latter was starting to show up during profiling.
		FontInstance poFontInstance = null;
		if (poAttr != null) {
			poFontInstance = poAttr.fontInstance();
		}

// Convert word line type into either none or all, depending whether this
// item is a space or part of a word.
		if (nType == GFXDecorationInfo.DECORATE_WORD) {
			if (bIsSpace) {
				nType = GFXDecorationInfo.DECORATE_NONE;
			} else {
				nType = GFXDecorationInfo.DECORATE_ALL;
			}
		}

// Baseline shift may change position of line:
//	Underline: pushed down by subscript
//	Line through: adjusts with any baseline shift, force break
//	Overline: pushed up by superscript

		UnitSpan oNewShift = oParm.offsetText();

		if ((poAttr != null)
		 && (poAttr.baselineShiftEnable())
		 && (! poAttr.baselineShift().isNeutral())) {
			TextBaselineShift oShifter = poAttr.baselineShift();
			oNewShift = oShifter.applyShift (oParm.offsetText(), oParm.offsetText());

			switch (meSpecial) {
				case SPECIAL_HONOUR_SUPERSCRIPT:
					if (oNewShift.gt (moBaseline)) {
						oNewShift = moBaseline; // ignore attempts to push baseline down
					}
					break;
				case SPECIAL_HONOUR_SUBSCRIPT:
					if (oNewShift.lt (moBaseline)) {
						oNewShift = moBaseline; // ignore attempts to push baseline up
					}
					break;
			}
		}

// If breaking on all changes (line-through), handle restoration of
// baseline as a breaking change.
		if (meSpecial == SPECIAL_BREAK_ON_ALL_CHANGES) {
			if (oNewShift != moBaseline) {
				bBreak = true;
			}
		}

		if ((nFlags & DECORATION_SUPPRESS) != 0) {
			nType = GFXDecorationInfo.DECORATE_NONE;
			nCount = 0;
		}

// Any simple change in type or count also breaks.	Note that type has
// been resolved into either none or all by this time.
		if ((nType != mnType) || (nCount != mnCount)) {
			bBreak = true;
		}

// Check if line type breaks on all changes (line through) and font
// ascent changes.	This would result in a different vertical line
// position.
		else if (meSpecial == SPECIAL_BREAK_ON_ALL_CHANGES) {
			if (poFontInstance != null) {
				if (! poFontInstance.getAscent().equals (Units.toUnitSpan (moHeight.ascent()))) {
					bBreak = true;
				}
			}
		}

// Change in line state: flush the currently accumulating line (if there
// is one) and start accumulating the new state.
		if (bBreak) {
			flush (oParm, true, oDecorations);

			mnType = nType;
			mnCount = nCount;

			onLineStateChange(); // notify derived class

			moStart = oLeft;
		}

		if (nType == GFXDecorationInfo.DECORATE_NONE) {
			return;
		}

		moHeight.accumulate (poAttr,
							 ((nFlags & DECORATION_VERTICAL) != 0) ? GFXGlyphOrientation.VERTICAL
									 							   : GFXGlyphOrientation.HORIZONTAL,
							 false);
		moBaseline = oNewShift; // for next time

// We need to track two ends for the current span: the last non-space and
// the last space.	If the span currently ends in a space, we normally
// _do_ want to apply the line, because we can get here only if the type
// started out as all (as opposed to becomming all due to word line
// handling).  However, We never want to underline the spaces that
// (don't) appear at the ends of lines where word-wrapping occurs.
		if (bIsSpace) {
			moSpace = oRight;
		} else {
			moNonSpace = oRight;
		}
	}

	void fontChange (DrawParm oParm, Map oDecorations) {
		if (meSpecial == SPECIAL_BREAK_ON_ALL_CHANGES) {
			flush (oParm, true, oDecorations);
		}
	}

	void complete (DrawParm oParm, Map oDecorations, boolean bIncludeSpaces) {
		flush (oParm, bIncludeSpaces, oDecorations);
	}

	static boolean hasDecoration (TextAttr poAttr) {
		if (poAttr == null) {
			return false;
		}

		return hasDecoration (poAttr.underline())
			|| hasDecoration (poAttr.overline())
			|| hasDecoration (poAttr.strikeout());
	}

	static boolean hasDecoration (int nDecoration) {
		return GFXDecorationInfo.extractDecoration (nDecoration) != GFXDecorationInfo.decorateNone;
	}

	Decoration (DispLineWrapped poLine, DrawParm oParm) {
		mnType = GFXDecorationInfo.DECORATE_NONE;
		mnCount = 0;
		moBaseline = oParm.offsetText();
		moHeight.setLegacyLevel (poLine.getLegacyLevel());
	}

	void setSpecial (int eSpecial) {
		meSpecial = eSpecial;
	}

	void setUseDescent (boolean bUseDescent) {
		mbUseDescent = bUseDescent;
	}

	void setFactors (double dWidth, double dOffset, double dOffset2) {
		mdWidth = dWidth;

		if (mbUseDescent) {
			mdOffset = dOffset;
			mdOffset2 = dOffset2;
		} else {
			mdOffset = -dOffset;
			mdOffset2 = -dOffset2;
		}
	}
	void setFactors (double dWidth, double dOffset) {
		setFactors (dWidth, dOffset, 0.0);
	}

	int getCount () {
		return mnCount;
	}

	abstract int getLineState (TextAttr poAttr);
	abstract void onLineStateChange ();

	private void flush (DrawParm oParm, boolean bIncludeSpaces, Map oDecorations) {
		if (mnType != GFXDecorationInfo.DECORATE_NONE) {
			float oEnd = moNonSpace;
			if (bIncludeSpaces && (moSpace > moNonSpace)) {
				oEnd = moSpace;
			}

			if (oEnd > moStart) {
				
				UnitSpan oScale = null;
				if (mbUseDescent) {
					oScale = Units.toUnitSpan (moHeight.descent());
					if (oScale.value() == 0) {
						oScale = new UnitSpan (moHeight.ascent() / 3.0, UnitSpan.POINTS_1K);
					}
				} else {
					oScale = Units.toUnitSpan (moHeight.ascent());
				}

				DecorationKey oKey = new DecorationKey(moStart, oEnd);

				UnitSpan width = oScale.multiply (mdWidth);
				UnitSpan halfWidth = width.divide(2);

				UnitSpan yOffset = calculateOffset (oScale, mdOffset, halfWidth);
				oDecorations.put (oKey, new DecorationValue(yOffset, width));

				if (mnCount == 2) {
					UnitSpan yOffset2 = calculateOffset (oScale, mdOffset2, halfWidth);
					oDecorations.put (oKey, new DecorationValue(yOffset2, width));
				}
			}
		}

		mnType = GFXDecorationInfo.DECORATE_NONE;
		mnCount = 0;
		moHeight.reset();
		moBaseline = oParm.offsetText();
	}

	private UnitSpan calculateOffset (UnitSpan scale, double offset, UnitSpan halfWidth) {
		UnitSpan scaledOffset = scale.multiply (offset);
		UnitSpan quarterWidth = halfWidth.divide (2);
		UnitSpan relativeOffset = scaledOffset.add (quarterWidth);
		return moBaseline.add (relativeOffset);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy