com.adobe.xfa.text.FormatInfo 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 java.util.ArrayList;
import java.util.List;
import com.adobe.xfa.ut.Rect;
import com.adobe.xfa.ut.Storage;
import com.adobe.xfa.ut.UnitSpan;
/**
* @exclude from published api.
*/
class FormatInfo {
static final int COMMIT_OK = 0;
static final int COMMIT_FULL = 1;
static final int COMMIT_RETRY = 2;
private final TextSparseStream mpoStream;
private final TextDisplay mpoDisplay;
private final DispChange moChange;
private final boolean mbUpdate;
private final AFERun mAFERun; // TODO: cache single instance in text context?
private MappingManager mMappingManager;
private final List moTabs;
private TextFrame mpoFrame;
private int mnFrameIndex;
private int mnStartFrame;
private int mnSuspectFrameIndex;
private Storage mpoLines;
private int mnOldSize;
private int mnNewSize;
private int mnStartLine;
private int mnNominalStartLine;
private int mnFirstUnchanged;
private final PosnStack moPositions = new PosnStack();
private TextAttr mpoDefaultAttr;
private TextAttr mpoAttr;
private int meJustH;
private int meJustV;
private boolean mbFirstLine;
private boolean mbNewPara;
private boolean mbRTL;
private UnitSpan moHeight;
private boolean mbFits;
private boolean mbUpdateConnect;
private boolean mbAllInitialLayout;
private boolean mbStoppedShort;
private Rect moOldExtent;
private boolean mbPostLayoutPending;
FormatInfo (TextDisplay poDisplay, List oTabs, boolean bUpdate, int eJustH, int eJustV, int nSuspectFrameIndex, TextAttr poDefaultAttr) {
mpoStream = poDisplay.stream();
mpoDisplay = poDisplay;
moChange = poDisplay.getChange();
mAFERun = new AFERun (poDisplay.getContext());
moTabs = oTabs;
mnSuspectFrameIndex = nSuspectFrameIndex;
mpoDefaultAttr = poDefaultAttr;
meJustH = eJustH;
meJustV = eJustV;
mbUpdate = bUpdate;
mbFirstLine = true;
mbNewPara = true;
mbRTL = poDisplay.isRTL();
mbFits = true;
int i;
int nFrames = mpoStream.getFrameCount();
moPositions.add (new TextPosn());
if (moChange.type() == DispChange.CHANGE_NONE) {
mpoStream.forceFrame (0);
setStartFrame (0);
}
if (moChange.type() != DispChange.CHANGE_NONE) { // may have changed
// If doing a forced frame relayout, flag as if incremental layout so
// that TextSparseStream::TrimFramesOnReflow() doesn't get called
// later.
if (moChange.type() == DispChange.CHANGE_FRAME) {
mbStoppedShort = true;
}
// If this is an update, there's no need to re-layout text from the start
// of the stream to the start of the change. This block attrmpts to find
// the starting line of the change.
if ((moChange.type() < DispChange.CHANGE_FULL) && mbUpdate) {
boolean bFoundStart = false;
// First optimization: Typing often occurs at the end of the stream.
// Check the last loaded frame to see if this is an insert at its end, by
// looping backward through the frames.
for (i = nFrames; i > 0; ) {
i--;
TextFrame poFrame = mpoStream.getFrame (i);
if (poFrame != null) {
int nLines = poFrame.getLineCount();
if (poFrame.isInsertAtEnd (moChange, nLines - 1)) {
setStartFrame (i, nLines - 1, true);
bFoundStart = true;
}
break;
}
}
// Not an insert at the end of the last loaded frame: Try to find the
// line that contains the start of the change.
if (! bFoundStart) {
TextPosnBase oChangeStart = new TextPosnBase (moChange.stream(), moChange.index());
TextDisplay.FindCaretInfo info = mpoDisplay.findCaretLine (oChangeStart);
if (info != null) {
TextFrame poFrame = mpoStream.getFrame (info.mFrameIndex);
setStartFrame (info.mFrameIndex,
info.mLineIndex,
(poFrame != null) && poFrame.isInsertAtEnd (moChange, info.mLineIndex));
}
}
}
// If this is the initial "layout" of frames created from text layout
// objects, there is nothing to do now. Try to detect that condition
// here.
else if ((moChange.type() == DispChange.CHANGE_FULL) && (! mbUpdate) && (nFrames > 0)) {
for (i = 0; i < nFrames; i++) {
TextFrame poFrame = mpoStream.getFrame (i);
if ((poFrame != null) && (poFrame.getLayoutState() == TextFrame.LAYOUT_NORMAL)) {
break;
}
}
if (i == nFrames) {
mbAllInitialLayout = true;
return;
}
setStartFrame (i);
}
if (mpoFrame == null) {
setStartFrame (0);
}
// Set up for partial/full reformat. Note that both insertion of spacess
// and deletions may cause part of the first changed line to now fit in
// the previous line. The adjusted start line is the line before the one
// with the change (unless the change is in the first line).
int nStartLine = getAdjustedStartLine();
if ((nStartLine > 0) || (mnFrameIndex > 0)) {
// Watson 1407512: If an incremental update, the starting line may start
// in an embedded field. In such a case, need to build up the position
// stack to the state that it would have been in if formatting started
// from the top.
TextPosnBase oLinePosn = getLine(nStartLine).getStartPosition();
moPositions.set (moPositions.size() - 1, new TextPosn (oLinePosn));
TextStream poStream = oLinePosn.stream();
while (poStream != mpoStream) {
TextPosn poParentPosn = poStream.position();
assert (poParentPosn != null);
TextPosn oParentNext = poParentPosn;
int eNext = oParentNext.next(); // skip over field ref
assert (eNext == TextItem.FIELD);
moPositions.add (0, oParentNext);
poStream = oParentNext.stream();
}
mbFirstLine = false;
} else {
moPositions.top().associate (mpoStream);
}
setAttr (moPositions.top().attributePtr());
}
preLayout();
}
void releaseOldLines () {
int i;
for (i = 0; i < mnNewSize; i++) {
DispLineWrapped poNew = getNew (i);
DispLineWrapped poOld = poNew.getOldLine();
if (poOld != null) {
poNew.setOldLine (null);
mpoFrame.releaseWrappedLine (poOld);
}
}
for (i = mnNewSize; i < mnOldSize; i++) {
mpoFrame.releaseWrappedLine (getLine (i));
}
mpoLines.setSize (mnNewSize);
}
TextAttr resolveAttr (TextAttr poSource) {
TextAttr poResult = null;
if (mpoDefaultAttr == null) {
poResult = poSource;
} else if (poSource == null) {
poResult = mpoDefaultAttr;
} else if (poSource.isComplete()) {
poResult = poSource;
} else {
poResult = new TextAttr (poSource);
poResult.addDisabled (mpoDefaultAttr);
}
return poResult;
}
DispChange getChange () {
return moChange;
}
boolean isUpdate () {
return mbUpdate;
}
AFERun getAFERun () {
return mAFERun;
}
MappingManager getMappingManager () {
return mMappingManager;
}
void setMappingManager (MappingManager manager) {
mMappingManager = manager;
}
TextFrame getFrame () {
return mpoFrame;
}
int getSuspectFrameIndex () {
return mnSuspectFrameIndex;
}
TextAttr getAttr () {
return mpoAttr;
}
TextAttr getDefaultAttr () {
return mpoDefaultAttr;
}
void setAttr (TextAttr poAttr) {
if (poAttr != null) {
mpoAttr = poAttr;
}
}
int getJustH () {
return meJustH;
}
int getJustV () {
return meJustV;
}
DispLineWrapped getLine (int nIndex) {
return mpoLines.get (nIndex);
}
int getOldSize () {
return mnOldSize;
}
DispLineWrapped getOld (int nIndex) {
return getLine(nIndex).getOldLine();
}
int getNewSize () {
return mnNewSize;
}
DispLineWrapped getNew (int nIndex) {
return getLine (nIndex);
}
DispLineWrapped getLastChanged () {
return getLine ((mnFirstUnchanged == 0) ? 0 : mnFirstUnchanged - 1);
}
int getTabSize () {
return moTabs.size();
}
DispTab getTab (int nIndex) {
return moTabs.get (nIndex);
}
void addTab (DispTab poTab) {
moTabs.add (poTab);
}
boolean allInitialLayout () {
return mbAllInitialLayout;
}
int getAdjustedStartLine () {
return mnStartLine;
}
int commitLine (DispLineWrapped poLine) {
UnitSpan oNewHeight = moHeight.add (poLine.getBExtent());
if ((! mpoFrame.allowExtension())
&& (! mpoFrame.unlimitedHeight())
&& (mnFirstUnchanged > 0)) { // always put at least one line in frame
UnitSpan oFrameHeight = mpoFrame.isOrientationHorizontal() ? mpoFrame.maxHeight() : mpoFrame.maxWidth();
if (oNewHeight.gt (oFrameHeight)) {
mnNewSize = mnFirstUnchanged; // in case started on last line and no longer fits
TextFrame poOldFrame = mpoFrame;
if (! advanceFrame()) {
return COMMIT_FULL;
}
if (mpoFrame.isOrientationHorizontal()) {
if ((! poOldFrame.maxWidth().equals (mpoFrame.maxWidth()))
&& ((! poOldFrame.unlimitedWidth())
|| (! mpoFrame.unlimitedWidth()))) {
return COMMIT_RETRY;
}
} else {
if ((! poOldFrame.maxHeight().equals (mpoFrame.maxHeight()))
&& ((! poOldFrame.unlimitedHeight())
|| (! mpoFrame.unlimitedHeight()))) {
return COMMIT_RETRY;
}
}
poLine.setFrame (mpoFrame); // was pointing to prev frame
oNewHeight = poLine.getBExtent();
}
}
moHeight = oNewHeight;
if (mnFirstUnchanged < mnOldSize) {
poLine.setOldLine (getLine (mnFirstUnchanged));
mpoLines.set (mnFirstUnchanged, poLine);
} else if ((! mbUpdate) && (mnFirstUnchanged < mpoLines.size())) {
mpoLines.set (mnFirstUnchanged, poLine);
} else {
mpoLines.add (poLine);
}
mnFirstUnchanged++;
mnNewSize = mnFirstUnchanged;
mbFirstLine = false;
mbNewPara = false;
return COMMIT_OK;
}
int getFirstUnchangedIndex () {
return mnFirstUnchanged;
}
void finish () {
// Get the last frame to finish up.
postLayout();
// If this is an update and we've run off the end, trim any frames that
// are no longer required.
if (mbUpdate && (! mbStoppedShort) && (moChange.type() != DispChange.CHANGE_NONE)) {
mpoStream.trimFramesOnReflow (mnFrameIndex + 1);
}
// Run through the changed lines and and split any markers that are to be
// split over line breaks.
// Determine the start and limit (n+1) frames and lines for this process.
// The process starts one line after the first changed lines, as we look
// at the start of each line.
if (! mbAllInitialLayout) {
int nFrame = mnStartFrame;
int nFrameLimit = mnFrameIndex;
if (nFrameLimit < mpoStream.getFrameCount()) {
nFrameLimit++;
}
TextFrame poFrame = mpoStream.getFrame (nFrame);
assert (poFrame != null);
int nLine = getAdjustedStartLine() + 1;
int nLineLimit = mnFirstUnchanged;
if (nLineLimit > poFrame.getLineCount()) {
nLineLimit = poFrame.getLineCount();
}
List oMarkers = new ArrayList();
boolean bLayoutSuppressed = false;
// The loop iterates once for each changed line.
for (; ; ) {
// Determine whether we need to move to the next frame.
if (nLine >= nLineLimit) {
nFrame++;
if (nFrame >= nFrameLimit) {
break;
}
poFrame = mpoStream.getFrame (nFrame);
assert (poFrame != null);
nLine = 0;
nLineLimit = poFrame.getLineCount();
}
// Pick up the line and find any range markers at its start.
DispLineWrapped poLine = poFrame.getLine (nLine);
TextPosnBase oSplitPosn = poLine.getStartPosition();
oSplitPosn.enumerateMarkers (oMarkers, false, true);
// Run through those markers looking for ones that must be split on line
// boundaries.
for (int i = 0; i < oMarkers.size(); i++) {
TextMarker poMarker = oMarkers.get(i);
assert (poMarker.isRangeMarker());
if (poMarker.getSplitState() == TextMarker.SPLIT_INSERT_WRAP) {
// Process only markers that completely span the line start (i.e., don't
// just abut it). This is actually tested in ForceSplit(), but can lead
// to infinite recursion when dealing with baseline override markers.
TextRange oRange = poMarker.getRange();
if ((oRange.start().index() < oSplitPosn.index())
&& (oRange.end().index() > oSplitPosn.index())) {
// The actual split.
poMarker.forceSplit (oSplitPosn.index(), oSplitPosn.index(), TextMarker.SPLIT_REASON_WORD_WRAP);
}
}
}
nLine++;
}
// Now restore the display if any baseline override markers were
// encountered. This may cause a re-layout, which is necessary because
// vertical line spacing may have changed. Unfortunately it's not the
// most efficient approach.
// TBD: someday try to figure out how to do as lines are being laid out.
// TBD: need an exception-safe way to do this.
if (bLayoutSuppressed) {
mpoDisplay.popSuppressFormat();
}
}
}
boolean isFirstLine () {
return mbFirstLine;
}
boolean isNewPara () {
return mbNewPara;
}
void setNewPara (boolean bNewPara) {
mbNewPara = bNewPara;
}
boolean isRTL () {
return mbRTL;
}
void setRTL (boolean bRTL) {
mbRTL = bRTL;
}
boolean fits () {
return mbFits;
}
void setFits (boolean bFits) {
mbFits = bFits;
}
boolean updateConnect () {
return mbUpdateConnect;
}
void setUpdateConnect (boolean bUpdateConnect) {
mbUpdateConnect = bUpdateConnect;
}
PosnStack posnStack () {
return moPositions;
}
boolean canStopNow (TextPosnBase oPosn) {
if (! mbUpdate) {
return false;
}
if (mnFirstUnchanged <= mnNominalStartLine) {
return false;
}
if (mnFirstUnchanged >= mpoLines.size()) {
return false;
}
if ((moChange.type() == DispChange.CHANGE_FRAME)
|| (moChange.type() == DispChange.CHANGE_TO_END)
|| (moChange.type() == DispChange.CHANGE_FULL)
|| (moChange.type() == DispChange.CHANGE_FULL_FORCE_FRAMES)) {
return false;
}
DispLineWrapped poLine = getLine (mnFirstUnchanged);
if (poLine.getPositionCount() == 0) {
return false;
}
TextPosn oNextOldPosn = poLine.getPosition(0).pp();
if ((oPosn.stream() == oNextOldPosn.stream()) && (oPosn.index() == oNextOldPosn.index())) {
boolean bCanStopNow = false;
if ((moChange.type() == DispChange.CHANGE_INSERT) || (moChange.stream() != oPosn.stream())) {
bCanStopNow = true;
} else if ((moChange.type() == DispChange.CHANGE_OTHER) || (moChange.type() == DispChange.CHANGE_FRAME)) {
if (moChange.index() + moChange.count() < oPosn.index()) {
bCanStopNow = true;
}
} else if (moChange.index() < oPosn.index()) {
bCanStopNow = true;
}
if (bCanStopNow) {
mnNewSize = mpoLines.size();
mbStoppedShort = true;
return true;
}
}
return false;
}
Rect diff (int nCommonIndex, Rect oDiff) {
DispLineWrapped poOld = getOld (nCommonIndex);
return (poOld == null) ? null : getNew(nCommonIndex).diff (poOld, oDiff);
}
Rect diff (int nOldIndex, int nNewIndex, Rect oDiff) {
// The old line may have changed, in which case moLines[nOldIndex] is
// actually the newer version at that index and we have to find its older
// version. Alternatively, it may not have changed or may now be
// deleted, in which case it doesn't have an older version. We always
// use the current (new) version of the new line.
DispLineWrapped poOld = getLine (nOldIndex);
if (poOld.getOldLine() != null) {
poOld = poOld.getOldLine();
}
return getNew(nNewIndex).diff (poOld, oDiff);
}
Rect diffLast (Rect oDiff) {
return getNew(mpoLines.size()-1).diff (null, oDiff);
}
private void setStartFrame (int nFrameIndex, int nLineIndex, boolean bSuppressBackup) {
int nNominalLineIndex = nLineIndex;
if (! bSuppressBackup) {
TextFrame poFrame = mpoStream.forceFrame (nFrameIndex);
for (; ; ) {
if (nLineIndex > 0) {
nLineIndex--;
} else {
nNominalLineIndex = 0;
while (nFrameIndex > 0) {
nFrameIndex--;
poFrame = mpoStream.forceFrame (nFrameIndex);
if (poFrame.getLineCount() > 0) {
nNominalLineIndex = poFrame.getLineCount();
nLineIndex = nNominalLineIndex - 1;
break;
}
}
if (nNominalLineIndex == 0) {
break;
}
}
DispLineWrapped poLine = poFrame.getLine (nLineIndex);
if (poLine.getStartBreak() != DispLine.LINE_BREAK_HYPHEN) {
break;
}
}
}
mnStartFrame = nFrameIndex;
attachFrame (nFrameIndex);
setStartLine (nLineIndex, nNominalLineIndex);
}
private void setStartFrame (int nFrameIndex) {
setStartFrame (nFrameIndex, 0, false);
}
private void setStartLine (int nStartLine) {
setStartLine (nStartLine, nStartLine);
}
private void setStartLine (int nStartLine, int nNominalStartLine) {
mnStartLine = nStartLine;
mnNominalStartLine = nNominalStartLine;
mnFirstUnchanged = nStartLine;
if (mbUpdate && (mnFirstUnchanged < mpoLines.size())) {
DispLineWrapped poLine = getLine (mnFirstUnchanged);
mbNewPara = poLine.isFirstParaLine();
mbRTL = poLine.isRTL();
}
// TBD: come up with a better name than moHeight for this variable
moHeight = UnitSpan.ZERO;
if (mpoLines.size() > 0) {
DispLineWrapped poLine = getLine (mnStartLine);
DispLineWrapped poLine0 = getLine (0);
moHeight = poLine.getBMin().subtract (poLine0.getBMin());
}
}
private boolean advanceFrame () {
postLayout();
if (! attachFrame (mnFrameIndex + 1)) {
return false;
}
setStartLine (0);
preLayout();
return true;
}
private boolean attachFrame (int nFrameIndex) {
TextFrame poAttachFrame = mpoStream.forceFrame (nFrameIndex);
if (poAttachFrame == null) {
return false;
}
mnFrameIndex = nFrameIndex;
mpoFrame = poAttachFrame;
mpoLines = mpoFrame.getLines();
mnOldSize = 0;
mnNewSize = 0;
if (mbUpdate) {
if (moChange.type() == DispChange.CHANGE_NONE) {
mnNewSize = mpoLines.size(); // justification only
} else {
mnOldSize = mpoLines.size();
}
}
return true;
}
private void preLayout () {
moOldExtent = mpoFrame.preLayout (this);
mbPostLayoutPending = true;
}
private void postLayout () {
if (mbPostLayoutPending) {
mpoFrame.postLayout (this, moOldExtent, false);
}
mbPostLayoutPending = true;
}
}