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

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

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

import com.adobe.xfa.gfx.GFXDriver;
import com.adobe.xfa.gfx.GFXGlyphOrientation;
import com.adobe.xfa.gfx.GFXLineAttr;

import com.adobe.xfa.ut.CoordPair;
import com.adobe.xfa.ut.Rect;
import com.adobe.xfa.ut.UnitSpan;

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

abstract class LeaderFill {
	private final DrawParm moDrawParm;		// line's draw parameters
	private boolean mbSetup;				// has this object been set up properly?

	private DispLineWrapped mpoLine;		// line containing the tab
	private TextAttr mpoAttr;				// attributes in effect at the tab
	private DispTab mpoDispTab; 			// actual tab object
	private GlyphLoc mpoGlyphLoc;			// glyph location object corresponding to the tab

	private UnitSpan moBasePatternWidth;	// width of the raw pattern
	private UnitSpan moPatternWidth = UnitSpan.ZERO;	// width including space between pattern iterations
	private UnitSpan moPatternStart = UnitSpan.ZERO;	// logical pattern X offset
	private UnitSpan moTabX;				// X coordinate of left side of tab
	private UnitSpan moTabWidth;			// width of the tab
	private UnitSpan moTextOffset;			// text baseline

	private boolean mbRenderGlyph;			// render the glyph in the parent line?
	private boolean mbSuppressAlign;		// suppress page alignment?

	LeaderFill (DrawParm oDrawParm) {
		moDrawParm = oDrawParm;
	}

	boolean setup (DispLineWrapped poLine, TextAttr poAttr, DispTab poDispTab, GlyphLoc poGlyphLoc) {
// The primary purpose of this method is to establish the various
// measurements required for filling the tab.  Some of those measurements
// are "logical" in nature.  A logical measurement is:
//		- measured relative to the tab
//		- inverted on the tab's X axis for RTL content
// In other words, a logical X offset ov zero corresponds to the left
// side of an LTR tab and the right side of an RTL tab.  Logical
// coordinates then increase moving away from the zero offset, into the
// tab's space.  The only cached logical measurement is the pattern start
// (mpPatternStart), which is assigned a value in this method.

// General initializations.
		mpoLine = poLine;
		mpoAttr = poAttr;
		mpoDispTab = poDispTab;
		mpoGlyphLoc = poGlyphLoc;

		moTabWidth = poDispTab.getFillWidth();
		if (moTabWidth.value() <= 0) {
			return false;
		}

// Pick up the glyph and establish the tab position in this line,
// includint the line's A (X) offset.
		Glyph oGlyph = mpoLine.getGlyph (poGlyphLoc.getGlyphIndex());
		float oTabXBase = oGlyph.getDrawX (mpoLine) + Units.toFloat (mpoLine.getAMin());
		moTabX = Units.toUnitSpan (oTabXBase);
		moTextOffset = Units.toUnitSpan (mpoLine.dispHeight().textOffset (GFXGlyphOrientation.HORIZONTAL)); // TBD:

		if (poAttr.leaderPatternWidthEnable()) {
			moPatternWidth = poAttr.leaderPatternWidth().getLength();
		}

// Invoke derived class initializations once basic initializations are
// performed at this level.  If this returns false, there is no leader to
// be rendered.
		if (! onSetup()) {
			return false;
		}

// The (full) pattern width must be at least the base pattern width set
// by the derived class.
		if (moPatternWidth.lt (moBasePatternWidth)) {
			moPatternWidth = moBasePatternWidth;
		}

// Page alignment: Find the "start" of the tab in absolute page
// coordinates.  The delta between the tab start and the appropriate page
// edge must be gridded up for the logical first pattern start.
		if (poAttr.leaderAlignEnable()
		 && (poAttr.leaderAlign() == TextAttr.LEADER_ALIGN_PAGE)
		 && (! mbSuppressAlign)
		 && (moPatternWidth.value() > 0)) {
			CoordPair oAbsPoint = driver().offset (false);
			oAbsPoint = new CoordPair (oAbsPoint.x().add (moTabX), oAbsPoint.y().add (textOffset()));
			oAbsPoint = driver().rotatePoint (oAbsPoint);

			UnitSpan oLeft = UnitSpan.ZERO;
			UnitSpan oTop = UnitSpan.ZERO;
			UnitSpan oRight = UnitSpan.ZERO;
			UnitSpan oBottom = UnitSpan.ZERO;
			Rect oPage = moDrawParm.drawInfo().getPage();
			if (oPage != null) {
				oLeft = oPage.left();
				oTop = oPage.top();
				oRight = oPage.right();
				oBottom = oPage.bottom();
			}
			
			boolean bRTL = mpoLine.isRTL();

			UnitSpan oRawStart;
			int nDegrees = driver().angle(false).degrees();
			if ((45 <= nDegrees) && (nDegrees < 135)) {
				oRawStart = bRTL ? oAbsPoint.y().subtract (oTop) : oBottom.subtract (oAbsPoint.y());	// 90 degrees
			}
			else if ((135 <= nDegrees) && (nDegrees < 225)) {
				oRawStart = bRTL ? oAbsPoint.x().subtract (oLeft) : oRight.subtract (oAbsPoint.x());	// 180 degrees
			}
			else if ((225 <= nDegrees) && (nDegrees < 315)) {
				oRawStart = bRTL ? oBottom.subtract (oAbsPoint.y()) : oAbsPoint.y().subtract (oTop);	// 270 degrees
			}
			else {
				oRawStart = bRTL ? oRight.subtract (oAbsPoint.x()) : oAbsPoint.x().subtract (oLeft);	// 0 degrees
			}

			if (bRTL) {
				oRawStart = oRawStart.subtract (moTabWidth);		// right edge of tab
			}

			UnitSpan oGrid = new UnitSpan (moPatternWidth.units(), -moPatternWidth.value());
			moPatternStart = oRawStart.grid (oGrid);
			moPatternStart = moPatternStart.subtract (oRawStart);
		}

// If not even a single instance fits, treat as no leader.
		if (moPatternStart.add(moBasePatternWidth).gt (moTabWidth)) {
			return false;
		}

// Align the (base) pattern (width) within the available pattern width,
// depending on alignment mode and line direction.
		UnitSpan oPatternOffset = UnitSpan.ZERO;
		UnitSpan oPatternGap = moPatternWidth.subtract (moBasePatternWidth);
		switch (alignment()) {
			case TextAttr.JUST_H_LEFT:
			case TextAttr.JUST_H_COMB_LEFT:
				if (mpoLine.isRTL()) {
					oPatternOffset = oPatternGap;
				}
				break;
			case TextAttr.JUST_H_CENTRE:
			case TextAttr.JUST_H_COMB_CENTRE:
				oPatternOffset = oPatternGap.divide (2);
				break;
			case TextAttr.JUST_H_RIGHT:
			case TextAttr.JUST_H_COMB_RIGHT:
				if (! mpoLine.isRTL()) {
					oPatternOffset = oPatternGap;
				}
				break;
		}
		moPatternStart = moPatternStart.add (oPatternOffset);

		assert (moPatternWidth.value() > 0);

		mbSetup = true;
		return true;
	}

	boolean render () {
		assert (mbSetup);

// The loop renders instances of the pattern in logical text progression
// order.  In other words, instance progression in an RTL leader will be
// right to left.  The pattern start and end are in tab logical
// co-ordinates (see the Setup() method above).  The X offset passed to
// the derived class is in text oject-relative co-ordinates.  The content
// of a single instance of the leader pattern is always rendered LTR.
		UnitSpan oPatternEnd = moPatternStart.add (moBasePatternWidth);
		do {
			UnitSpan oOffsetX;
			if (mpoLine.isRTL()) {
				oOffsetX = moTabWidth.subtract (oPatternEnd);
			} else {
				oOffsetX = moPatternStart;
			}
			onRender (moTabX.add (oOffsetX));
			moPatternStart = moPatternStart.add (moPatternWidth);
			oPatternEnd = moPatternStart.add (moBasePatternWidth);
		} while (oPatternEnd.lt (moTabWidth));

		mbSetup = false;
		return mbRenderGlyph;
	}

	DispLineWrapped line () {
		return mpoLine;
	}

	TextAttr attr () {
		return mpoAttr;
	}

	DrawParm drawParm () {
		return moDrawParm;
	}

	DispTab dispTab () {
		return mpoDispTab;
	}

	GlyphLoc glyphLoc () {
		return mpoGlyphLoc;
	}

	UnitSpan basePatternWidth () {
		return moBasePatternWidth;
	}

	void setBasePatternWidth (UnitSpan oBasePatternWidth) {
		moBasePatternWidth = oBasePatternWidth;
	}

	UnitSpan patternWidth () {
		return moPatternWidth;
	}

	void setPatternWidth (UnitSpan oPatternWidth) {
		moPatternWidth = oPatternWidth;
	}

	UnitSpan patternStart () {
		return moPatternStart;
	}

	void setPatternStart (UnitSpan oPatternStart) {
		moPatternStart = oPatternStart;
	}

	UnitSpan tabX () {
		return moTabX;
	}

	void setTabX (UnitSpan oTabX) {
		moTabX = oTabX;
	}

	UnitSpan tabWidth () {
		return moTabWidth;
	}

	void setTabWidth (UnitSpan oTabWidth) {
		moTabWidth = oTabWidth;
	}

	UnitSpan textOffset () {
		return moTextOffset;
	}

	void setTextOffset (UnitSpan oTextOffset) {
		moTextOffset = oTextOffset;
	}

	boolean renderGlyph () {
		return mbRenderGlyph;
	}

	void setRenderGlyph (boolean bRenderGlyph) {
		mbRenderGlyph = bRenderGlyph;
	}

	boolean suppressAlign () {
		return mbSuppressAlign;
	}

	void setSuppressAlign (boolean bSuppressAlign) {
		mbSuppressAlign = bSuppressAlign;
	}

	GFXDriver driver () {
		return moDrawParm.driver();
	}

	int alignment () {
		return mpoAttr.justifyH();
	}

	abstract boolean onSetup ();
	abstract void onRender (UnitSpan oOffsetX);
}

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

class LeaderRule extends LeaderFill {
	private static final double RULE_DASH_PATTERN_WIDTH_RATIO = 0.666667;
	private static final double RULE_DASH_THICKNESS_RATIO = 4.0;
	private static final double RULE_WIDTH_THICKNESS_RATIO = 6.0;
	private final DrawAttr moDrawAttr;
	private GFXLineAttr moLineAttr;
	private UnitSpan moRuleThickness;

	LeaderRule (DrawParm oDrawParm, DrawAttr oDrawAttr) {
		super (oDrawParm);
		moDrawAttr = oDrawAttr;
	}

	boolean onSetup () {
		assert (attr().leaderPattern() == TextAttr.LEADER_PATTERN_RULE);

		if (! attr().ruleStyleEnable()) {
			return false;
		}

		if (! attr().ruleThicknessEnable()) {
			return false;
		}
		moRuleThickness = attr().ruleThickness().getLength();
		if (moRuleThickness.value() <= 0) {
			return false;
		}

		switch (attr().ruleStyle()) {
			case TextAttr.RULE_STYLE_SOLID:
				setBasePatternWidth (tabWidth());
				setPatternWidth (tabWidth());
				setSuppressAlign (true);
				break;

			case TextAttr.RULE_STYLE_DOTTED: {
				setBasePatternWidth (moRuleThickness);
				UnitSpan oWidth = moRuleThickness.multiply (2);
				if (patternWidth().lt (oWidth)) {
					setPatternWidth (oWidth);
				}
				break;
			}

			case TextAttr.RULE_STYLE_DASHED:
				if (patternWidth().value() <= 0) {
					setBasePatternWidth (moRuleThickness.multiply (RULE_DASH_THICKNESS_RATIO));
					setPatternWidth (moRuleThickness.multiply (RULE_WIDTH_THICKNESS_RATIO));
				} else {
					setBasePatternWidth (patternWidth().multiply (RULE_DASH_PATTERN_WIDTH_RATIO));
				}
				break;

			default:
				return false;
		}

		setRenderGlyph (true);
		return true;
	}

	void onRender (UnitSpan oOffsetX) {
		if (moLineAttr == null) {
			moLineAttr = new GFXLineAttr();
			moLineAttr.colour (moDrawAttr.getGfxAttr().colour());
			moLineAttr.colourBg (moDrawAttr.getGfxAttr().colour());
			moLineAttr.style (GFXLineAttr.STYLE_SOLID);
			moLineAttr.width (moRuleThickness);
			moLineAttr.hand (GFXLineAttr.HAND_EVEN);
			moLineAttr.cap (GFXLineAttr.CAP_BUTT);
			driver().lineAttr (moLineAttr);
		}

		CoordPair oStart = new CoordPair (oOffsetX, textOffset());
		oStart = ABXY.toXY (line().getXYOrigin(), oStart, line().frame().getLayoutOrientation());

		CoordPair oEnd = new CoordPair (oOffsetX.add (basePatternWidth()), textOffset());
		oEnd = ABXY.toXY (line().getXYOrigin(), oEnd, line().frame().getLayoutOrientation());

		driver().relLine (oStart, oEnd);
	}
}

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

class LeaderContent extends LeaderFill {
	private final TextDrawInfo moDrawInfo;
	private final int mnCharIndex;

	private TextRegion mpoLeaderRegion;
	private TextDisplay mpoLeaderDisplay;
	private DispLineWrapped mpoLeaderContentLine;
	private DrawParm mpoLeaderParm;

	private TextSelection mpoPrimary;
	private TextSelection mpoSecondary;

	private UnitSpan moOffsetY;

	LeaderContent (DrawParm oDrawParm, int nCharIndex) {
		super (oDrawParm);
		moDrawInfo = new TextDrawInfo (oDrawParm.env());
		mnCharIndex = nCharIndex;
	}

	int alignment () {
		if (mpoLeaderRegion != null) {
			TextPosnBase oPosn = new TextPosnBase (mpoLeaderRegion);
			TextAttr poAttr = oPosn.attributePtr();
			if ((poAttr != null) && poAttr.justifyHEnable()) {
				return poAttr.justifyH();
			}
		}

		return super.alignment();
	}

	boolean onSetup () {
		assert ((attr().leaderPattern() == TextAttr.LEADER_PATTERN_DOTS)
			 || (attr().leaderPattern() == TextAttr.LEADER_PATTERN_USE_CONTENT));

		mpoLeaderRegion = dispTab().getFillRegion();
		if (mpoLeaderRegion == null) {
			return false;
		}

		mpoLeaderDisplay = mpoLeaderRegion.display();
		if (mpoLeaderDisplay == null) {
			return false;
		}

		setBasePatternWidth (mpoLeaderDisplay.runtimeExtent (true).right());
		if (basePatternWidth().value() <= 0) {
			return false;
		}

		TextFrame poFrame = mpoLeaderRegion.getFrame (0);
		assert (poFrame != null);
		assert (poFrame.getLineCount() == 1);
		mpoLeaderContentLine = poFrame.getLine (0);

		mpoPrimary = resolveTabSelection (mpoLeaderRegion, mnCharIndex, drawParm().primary());
		mpoSecondary = resolveTabSelection (mpoLeaderRegion, mnCharIndex, drawParm().secondary());

		moDrawInfo.setPrimary (mpoPrimary);
		moDrawInfo.setSecondary (mpoSecondary);
		moDrawInfo.setPage (drawParm().page());

		assert (mpoLeaderParm == null);
		mpoLeaderParm = new DrawParm (moDrawInfo);
		mpoLeaderParm.setDriver (drawParm().driver());
		mpoLeaderParm.setCharIndex (mnCharIndex);

		moOffsetY = line().getBMin().add (textOffset());
		UnitSpan delta = Units.toUnitSpan (mpoLeaderContentLine.dispHeight().textOffset (GFXGlyphOrientation.HORIZONTAL)); // TBD:
		moOffsetY = moOffsetY.subtract (delta);

		return true;
	}

	void onRender (UnitSpan oOffsetX) {
		CoordPair oOffset = new CoordPair (oOffsetX, moOffsetY);
		driver().pushOffset (true, oOffset);

		try {
			Rect oFillInvalid = drawParm().invalid().subtract (oOffset);
			mpoLeaderParm.setInvalid (oFillInvalid);
			mpoLeaderContentLine.gfxDraw (mpoLeaderParm);
		} finally {
			driver().popOffset();
		}
	}

	private TextSelection resolveTabSelection (TextRegion poLeaderRegion, int nCharIndex, TextSelection poSourceSelection) {
		if (poSourceSelection == null) {
			return null;
		}

		DispPosn oPosn = line().getMappedPosition (nCharIndex);
		if (oPosn.pp().stream() != poSourceSelection.stream()) {
			return null;
		}

		int nStreamIndex = DispLine.charToStreamIndex (oPosn, nCharIndex, 0);
		if ((nStreamIndex < poSourceSelection.start().index()) || (nStreamIndex >= poSourceSelection.end().index())) {
			return null;
		}

		return new TextSelection (poSourceSelection.colour(), poSourceSelection.colourBg(), poLeaderRegion);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy