com.adobe.xfa.text.Decoration 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
The 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);
}
}