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

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

There is a newer version: 2024.11.18598.20241113T125352Z-241000
Show newest version
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;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy