com.adobe.xfa.text.LeaderFill Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
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);
}
}