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

com.adobe.xfa.text.DispRaw 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.font.FontInstance;
import com.adobe.xfa.font.FontItem;
import com.adobe.xfa.gfx.GFXGlyphOrientation;

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

class RawProcessor {
//----------------------------------------------------------------------
//
//		DispRawProcessor - Actual processing of raw data to
//		populate a raw line.
//
//----------------------------------------------------------------------

	private final DispLineRaw mpoLine;
	private final FormatInfo moFormatInfo;
	private final DispMapSpan moAnchor;
	private final DispMapSpan moRun;
	
	private TextAttr mpoPendingAttr;
	private int mnVisualCharCount;
	private boolean mbHasAmbiguousBreaks;
	private int[] moAccentRun;
	int mnAccentRunSize;
	private int meAccentRunBreak;
	private int meGlyphOrientation;
//	private boolean mbFit;		// TODO: not referenced

	private static class ValidationData {
		TextAttr originalAttr;
		FontInstance originalFontInstance;
		FontInstance fontInstance;
		boolean replaceFont;
	}

	RawProcessor (FormatInfo oFormatInfo, DispLineRaw poLine, TextPosnBase oInitialPosition, TextAttr poInitialAttr) {
		mpoLine = poLine;
		moFormatInfo = oFormatInfo;
		moAnchor = new DispMapSpan (poLine, new DispPosn (oInitialPosition));
		moRun = new DispMapSpan (poLine, new DispRun (poLine.frame(), poInitialAttr));
		
		mnVisualCharCount = 0;
		mbHasAmbiguousBreaks = false;
		moAccentRun = poLine.display().getContext().getAccentRun (0);
		meAccentRunBreak = 0;
		meGlyphOrientation = GFXGlyphOrientation.HORIZONTAL;
	}

	void processAttr (TextAttr poAttr) {
		flushAccentRun();

		poAttr = moFormatInfo.resolveAttr (poAttr);
		moFormatInfo.setAttr (poAttr);

		if ((poAttr != null) /* && poAttr.fontEnable() */ && poAttr.substituteFont()) {
			mpoLine.display().setFontSubstitution (true);
		}

		DispRun oNewRun = new DispRun (moRun.r(), moFormatInfo.getAttr());
		moRun.reset (oNewRun);

		mpoPendingAttr = null;
	}

	void processChar (int c, int eBreak, TextPosnBase poPosition) {
		if (mpoLine.frame().isOrientationVertical()) {
			int eGlyphOrientation = figureVerticalOrientationsFromChar (c);
			if (meGlyphOrientation != eGlyphOrientation) {
				flushAccentRun();
				DispRun oNewRun = new DispRun (moRun.r(), eGlyphOrientation);
				moRun.reset (oNewRun);
				meGlyphOrientation = eGlyphOrientation;
			}
		}

// Tab character: Embed a an object of type DispTab in the line at
// the character index of the tab.	The size of this object will be
// filled in during word-wrapping.
// TBD: what if tab in decomposition run?
		if (c == '\t') {
			syncPosition (poPosition);

			DispTab poTab = new DispTab();

			TextAttr poAttr = moRun.r().getAttr();
			if (poAttr != null) {
				poTab.setHeight (poAttr.size());
			}

			DispEmbed poDispEmbed = addEmbed (poTab, '\t', TextCharProp.defaultSpace);	// TODO: C++ implementation doesn't use complete set of properties
			if (poDispEmbed == null) {
				return;
			}

			DispTab poMappedTab = (DispTab) poDispEmbed.getEmbed();
			moFormatInfo.addTab (poMappedTab);
		}

// Watson #1245453 - don't allow Unicode direction change characters
// to be added to moAccentRun, as they can cause unwanted font changes
		else if ((c >= 0x202A) && (c <= 0x202F)) {
			syncPosition (poPosition);
			addChar (c, eBreak);
		}

// Normal character: Accumulate each sequence of base character plus zero
// or more accents into a run that can be processed as a unit.
		else {
			if (TextCharProp.getBreakClass (eBreak) != TextCharProp.BREAK_CM) {
				syncPosition (poPosition); // flushes current accent run and sets position
				meAccentRunBreak = eBreak;
			}

			int newSize = mnAccentRunSize + 1;
			if (newSize >= moAccentRun.length) {
				moAccentRun = mpoLine.display().getContext().getAccentRun (newSize);
			}
			moAccentRun[mnAccentRunSize] = c;	// TODO: rethink handling of accent run on Java
			mnAccentRunSize = newSize;
		}
	}

	void processObject (TextEmbed poEmbed, TextPosnBase poPosition) {
		syncPosition (poPosition);
		addEmbed (poEmbed);
	}

	void processPara (TextPosnBase poPosition) {
		syncPosition (poPosition);
		addChar (' ', TextCharProp.defaultSpace); // treat as new-line
		mpoPendingAttr = null;
		moFormatInfo.setNewPara (true);
	}

	void processStreamStart (TextPosnBase oPosition) {
		processStreamChange (oPosition);
	}

	void processStreamEnd (TextPosnBase oPosition) {
		processStreamChange (oPosition);
	}

	void finish (boolean bIsLastParaLine, boolean bIsLastLineInStream) {
		flushAccentRun();
		moAnchor.flush();
		moRun.flush();

		if (bIsLastParaLine) {
			mpoLine.setLastParaLine (DispLine.REAL_LAST_LINE);
		} else {
			mpoLine.setLastParaLine (DispLine.HARD_NEW_LINE);
		}
		mpoLine.setLastLineInStream (bIsLastLineInStream);
		mpoLine.setVisualCharCount (mnVisualCharCount);

		if (mbHasAmbiguousBreaks) {
			int eDefault = mpoLine.display().isIdeographic() ? TextCharProp.BREAK_ID : TextCharProp.BREAK_AL;
			int ePrev = TextCharProp.WIDTH_A;
			int eNext = TextCharProp.WIDTH_A;
			int nNextKnown = 0;

			for (int i = 0; i < mpoLine.getCharCount(); i++) {
				int nData = mpoLine.getBreakData (i);
				int eBreak = TextCharProp.getBreakClass (nData);
				int eWidth = TextCharProp.getWidthClass (nData);

				if (eBreak == TextCharProp.BREAK_AI) {
					eBreak = TextCharProp.resolveBreakWidth (eWidth, eWidth);
				}

				if (eBreak == TextCharProp.BREAK_AI) {
					if (nNextKnown <= i) {
						nNextKnown = i + 1;
						eNext = TextCharProp.WIDTH_A;
						while ((eNext == TextCharProp.WIDTH_A) && (nNextKnown < mpoLine.getCharCount())) {
							int eNextWidth = mpoLine.getWidthClass (nNextKnown);
							nNextKnown++;
							if (! TextCharProp.isAmbiguousWidth (eNextWidth)) {
								eNext = eWidth;
								break;
							}
						}
					}

					eBreak = TextCharProp.resolveBreakWidth (ePrev, eNext, eDefault);
					mpoLine.setBreak (i, TextCharProp.setBreakClass (nData, eBreak));

					if (eBreak == TextCharProp.BREAK_ID) {
						ePrev = TextCharProp.WIDTH_W;
					} else {
						ePrev = TextCharProp.WIDTH_N;
					}
				}

				else {
					if (! TextCharProp.isAmbiguousWidth (eWidth)) {
						ePrev = eWidth;
					}
				}
			}
		}
	}

	private void processStreamChange (TextPosnBase oPosition) {
		flushAccentRun();

		moAnchor.reset (new DispPosn (oPosition), true);

		TextAttr poAttr = moAnchor.pp().attributePtr();

		if ((poAttr == null) || (poAttr.fontInstance() == null)) {
			TextAttr poRunAttr = moRun.r().getAttr();
			if (poRunAttr == null) {
				poRunAttr = poAttr;
			}
			assert (poRunAttr != null);

			TextAttr poNewAttr = new TextAttr (true);

			poNewAttr.fontService (poRunAttr.fontService());
			poNewAttr.override (poRunAttr);

			poAttr = poNewAttr;
		}

		moFormatInfo.setAttr (poAttr);

		moRun.reset (new DispRun (moRun.r(), moFormatInfo.getAttr()));

		mpoPendingAttr = null;
	}

	private void syncPosition (TextPosnBase poPosition) {
		flushAccentRun();
		if (poPosition != null) {
			if ((moAnchor.pp().stream() != poPosition.stream())
			 || ((moAnchor.pp().index() + moAnchor.length()) != poPosition.index())) {
				moAnchor.reset (new DispPosn (poPosition));
			}
		}
	}

	private DispEmbed addEmbed (TextEmbed poEmbed, int cInsert, int eBreak) {
		moRun.flush();
		int nCharsBefore = mpoLine.getCharCount();
		if (cInsert == '\t') {
			assert (mnAccentRunSize == 0);
			if ((moAccentRun == null) || (moAccentRun.length < 1)) {
				moAccentRun = mpoLine.display().getContext().getAccentRun (1);
			}
			moAccentRun[0] = cInsert;	// TODO: rethink handling of accent run on Java
			mnAccentRunSize = 1;
			resolveAccentRunFont();
			mnAccentRunSize = 0;
		}
		addChar (cInsert, eBreak);
		DispEmbed oDispEmbed = new DispEmbed (poEmbed);
		mpoLine.add (oDispEmbed, nCharsBefore);
		moRun.flush();				// each embedded obj has its own run for consistency with C++ implementation
		return oDispEmbed;
	}
	private DispEmbed addEmbed (TextEmbed poEmbed) {
		return addEmbed (poEmbed, Pkg.EMBED_OBJ_CHAR, TextCharProp.defaultObject);
	}

	private void flushAccentRun () {
// This method flushes any run of base character plus zero or more
// accents to the line.
		if (mnAccentRunSize == 0) {
			return;
		}

// Resolve the fonts for the accent run.  This also returns the number of
// characters collapsed for AXTE ligatures.
		int nComposed = resolveAccentRunFont();
		int c = moAccentRun[0];

// Certain languages require special run breaks and properties.  Arabic
// requires common ligatures be enabled while Thai requires kerning.  In
// addition, Arabic requires special handling for comb fields.	Use
// character ranges to determine Arabic language, since we don't track it
// as a separate character property.
		boolean bIsArabic = false;
		boolean bIsThai = false;
		if (((c >= 0x0600) && (c <= 0x074F))			// Arabic, Arabic supplement, Syriac
		 || ((c >= 0x0780) && (c <= 0x07BF))			// Thaana
		 || ((c >= 0xFB50) && (c <= 0xFDFF))			// Arabic presentation forms A
		 || ((c >= 0xFE70) && (c <= 0xFEFF))) {			// Arabic presentation forms B
			bIsArabic = true;
		} else if ((c >= 0x0E00) && (c <= 0x0E7F)) {	// Thai
			bIsThai = true;
		}

// If this is a comb field, force a new run on every Arabic base
// character.  This is in case the text is Arabic; we want all characters
// in isolated form and they must be in separate runs for this to happen.
		if (mpoLine.getCombCells() > 0) {
			if (bIsArabic) {
				DispRun oNewRun = new DispRun (moRun.r());
				oNewRun.setComb (true);
				moRun.reset (oNewRun);
			}
		}

// Otherwise, use character's language to determine whether common
// ligatures or kerning are required or not and recycle the current run
// if necessary.
		else {
			boolean bAllowLigatures = bIsArabic;
			boolean bAllowKerning = bIsThai;
			TextAttr poAttr = moRun.r().getAttr();
			if (poAttr != null) {
				if (poAttr.ligatureEnable() && (poAttr.ligature() == TextAttr.LIGATURE_COMMON)) {
					bAllowLigatures = true;
				}
				if (poAttr.kerningEnable() && poAttr.kerning()) {
					bAllowKerning = true;
				}
			}
			if ((bAllowLigatures != moRun.r().allowLigatures()) || (bAllowKerning != moRun.r().allowKerning())) {
				DispRun oNewRun = new DispRun (moRun.r());
				oNewRun.setAllowLigatures (bAllowLigatures);
				oNewRun.setAllowKerning (bAllowKerning);
				moRun.reset (oNewRun);
			}
		}

// If there is an AXTE ligature, create a new position map entry that
// represents the composition, and flush the old entry.
		if (nComposed > 1) {
			DispPosn p = moAnchor.p();
			TextPosn pp = p.pp();
			int nAnchorStreamCount = (p.getStreamCount() == 0) ? moAnchor.length() : p.getStreamCount();
			DispPosn oNewPosn = new DispPosn (pp.stream(), pp.index() + nAnchorStreamCount, pp.position());
			oNewPosn.setStreamCount (nComposed);
			moAnchor.reset (oNewPosn);
		}

// We're now ready to add the character to the line.
		addChar (c, meAccentRunBreak);

		int eClass = TextCharProp.getBreakClass (meAccentRunBreak);
		if (eClass != TextCharProp.BREAK_SP) {
			if (eClass == TextCharProp.BREAK_AI) {
				mbHasAmbiguousBreaks = true;
			}
			mnVisualCharCount = mpoLine.getCharCount();
		}

// Run through any accents that follow the base character and add them as
// well.
		for (int nAccentIndex = 1; nAccentIndex < mnAccentRunSize; nAccentIndex++) {
			int cAccent = moAccentRun[nAccentIndex];
			if (cAccent != '\0') {
				addChar (cAccent, TextCharProp.getCharProperty (cAccent));
			}
			mnVisualCharCount = mpoLine.getCharCount();
		}

// Set up for next time.
		mnAccentRunSize = 0;
		moFormatInfo.setNewPara (false);
	}

	private int resolveAccentRunFont () {
// Confirm that this base/accent run is in the current font.  If not, try
// to find a font that does work.
// First, resolve a change to base character if the text is invisible.
		int nComposed = 1;
		int c = substituteInvisibleCharacter (moAccentRun[0]);
		if (c != moAccentRun[0]) {
			moAccentRun[0] = c;
			meAccentRunBreak = TextCharProp.getCharProperty (c);
		}

// Don't dork with fonts for zero-width characters and the embedded
// object character.
		if ((c == 0x200B) || (c == 0x200C) || (c == 0x200D) || (c == 0xFEFF) || (c == Pkg.EMBED_OBJ_CHAR)) {
			return 1;
		}
		if (c == '\t') {
			c = ' ';
		}

		ValidationData validation = new ValidationData();
		int nSplit = 1;
		//int nRemove = 0;

		TextAttr poAttr = moRun.r().getAttr();
		boolean bInvisible = false;
		if ((poAttr != null) && poAttr.invisibleEnable() && poAttr.invisible()) {
			bInvisible = true;
		}

// If this is a single base character (most common case) or is invisible,
// don't invoke the overhead of looking for precomposed forms; simply
// validate the character given.
		if ((mnAccentRunSize == 1) || bInvisible) {
			validateBaseChar (c, validation);
			if (validation.fontInstance == null) {
				return 1;
			}
		}

// A real accent run: WRServices may combine the base character plus some
// number of accents into a precomposed Unicode form before looking up
// glyph ID.  In order to get the correct font pre-validation, try to
// find a precomposed form in the font.  Start with the longest possible
// base+accent(s) sequence and then pop accents off the end until a match
// is found, indicated by variable nSplit.
		else {
			Composition oComposition = new Composition (moAccentRun);
			oComposition.reconcile();
			for (; ; ) {
				c = oComposition.getPrecomposedChar();
				validateBaseChar (c, validation);
				if (validation.fontInstance == null) {
					break;
				}
				if (oComposition.getSplit() == 1) {
					return 1; // can't even validate base char
				}

				oComposition.popCombiningMark(); // retry with shorter run
			}

			nSplit = oComposition.getSplit();
			if (oComposition.doComposition()) {
				assert (nSplit > 1);
				nComposed = nSplit;
				for (int i = 1; i < nSplit; i++) {
					moAccentRun[i] = moAccentRun[nSplit+i-1];
				}
				mnAccentRunSize -= nSplit - 1;
				moAccentRun[0] = c;
				meAccentRunBreak = TextCharProp.getCharProperty (c);
				nSplit = 1; // in case any unresolved accents after
			}
		}

// Run through any remaining accents, trying to validate them in the
// current font. If one is missing, try to find a font that represents it
// _and_ has all the (base+accent) characters tested so far.
		for (int nAccentIndex = nSplit; nAccentIndex < mnAccentRunSize; nAccentIndex++) {
			int cAccent = substituteInvisibleCharacter (moAccentRun[nAccentIndex]);
			moAccentRun[nAccentIndex] = cAccent;
			if ((cAccent != '\0') && (! validateChar (validation.fontInstance, cAccent))) {
				FontInstance oTestInstance = reconcileFont (validation.fontInstance, cAccent);
				if ((oTestInstance != null) && (oTestInstance != validation.fontInstance)) {
					int i = 0;
					if (validateChar (oTestInstance, c)) {
						for (i = nSplit; i < nAccentIndex; i++) {
							if (! validateChar (oTestInstance, moAccentRun[i])) {
								break;
							}
						}
					}
					if (i >= nAccentIndex) {
						validation.fontInstance = oTestInstance;
						validation.replaceFont = true;
					}
				}
			}
		}

// If there is a font change required (either for the base or at least
// one of the accent characters), create a new run with this font.
		if (validation.replaceFont) {
			TextAttr poOverrideAttr = new TextAttr (validation.originalAttr);

			boolean bSubstitute = false;
			FontItem poFontItem = validation.fontInstance.getFontItem();
			FontItem poOriginalItem = validation.originalFontInstance.getFontItem();
			if ( /* (poFontItem.getEncoding() != poOriginalItem.getEncoding())
			 || */ (! poFontItem.equals (poOriginalItem))) {
				bSubstitute = true;
			}
			poOverrideAttr.fontInstance (validation.fontInstance, poOriginalItem.getTypeface());
			if (bSubstitute) {
				mpoLine.display().setFontSubstitution (true);
			}

// Grab ref before changing run, because poOriginalAttr may be temporary.
			mpoPendingAttr = validation.originalAttr;

			DispRun oNewRun = new DispRun (moRun.r(), poOverrideAttr);
			moRun.reset (oNewRun);
		}

		return nComposed;
	}

	private void validateBaseChar (int c, ValidationData validation) {
		boolean bValidated = false;

// If there is no override in effect, there is only one attribute in
// which to validate the character.  Otherwise, there are two choices:
// the override (in moRun.gertAttr()), and the original (mpoPenfingAttr).
// In this case, try the original first, as we want to get back to the
// requested font if possible.	If that fails, we'll then try the
// override before looking for yet another font to use. // no override in effect ...
		if (mpoPendingAttr == null) {
			validation.originalAttr = moRun.r().getAttr();
		} else {
			validation.originalAttr = mpoPendingAttr;
			if (validateChar (mpoPendingAttr.fontInstance(), c)) {
				DispRun oNewRun = new DispRun (moRun.r(), mpoPendingAttr);
				moRun.reset (oNewRun);
				mpoPendingAttr = null;
				bValidated = true;
			}
		}

// Not validated at this point if there is no override, or there is an
// override and the character didn't validate in the original font.  Here
// we try to validate in the "current" font (original if no override, or
// override if one present).
		if (! bValidated) {
			TextAttr poAttr = moRun.r().getAttr();
			if (poAttr == null) {
				poAttr = validation.originalAttr;
			}
			if (poAttr != null) {
				bValidated = validateChar (poAttr.fontInstance(), c);
			}
		}

// Establish the initial font instance.  If there's no need for a run
// change, this is simply the current run's font.  Otherwise, try to find
// a font that has the base character.
		if (bValidated) {
			TextAttr poAttr = moRun.r().getAttr();
			if (poAttr != null) {
				validation.originalFontInstance = poAttr.fontInstance();
				validation.fontInstance = validation.originalFontInstance;
			}
		} else {
			assert (validation.originalAttr != null);
			validation.originalFontInstance = validation.originalAttr.fontInstance();
			validation.fontInstance = reconcileFont (validation.originalFontInstance, c);
			validation.replaceFont = true;
		}
	}

	private boolean validateChar (FontInstance oFontInstance, int c) {
		if (oFontInstance != null) {
//			try {
				FontItem poFontItem = oFontInstance.getFontItem();
				if ((poFontItem != null) && (poFontItem.validateChar (c, GFXGlyphOrientation.usesHorizontalGlyphs (meGlyphOrientation)))) {
					return true;	// TODO:
				}
//			}
//			catch (11) {
//			}
		}
//
		return false;
	}

	private FontInstance reconcileFont (FontInstance oFontInstance, int c) {
		TextAttr poAttr = moRun.r().getAttr();
		if (poAttr == null) {
			return null;
		}

//		try {
			return poAttr.gfxSource().reconcileFont (oFontInstance, c);
//		}
//		catch (11) {
//		}
//		return FontInstance();
	}

	private int substituteInvisibleCharacter (int c) {
		TextAttr poAttr = moRun.r().getAttr();
		if (poAttr != null) {
			if (poAttr.invisibleEnable() && poAttr.invisible()) {
				if (poAttr.invisCharEnable() && (poAttr.invisChar() != '\0')) {
					c = poAttr.invisChar();
				}
			}
		}

		return c;
	}

	private void addChar (int c, int eBreak) {
		mpoLine.addChar (c, eBreak);
	}

	private static boolean charIsRomanRotateable (int character) {
//		CharRangeCode eCharRange = CharConverter.getCharRange (character);
//		return CharConverter.isCharRangeLatin (eCharRange) || ((character >= 0xFB00) && (character < 0xFB07)) || ((character >= 0xFF61) && (character <= 0xFF9F));
		return ((character >= 0x41) && (character < 0x5A))
			|| ((character >= 0x61) && (character < 0x7A))
			|| ((character >= 0xFB00) && (character < 0xFB07))
			|| ((character >= 0xFF61) && (character <= 0xFF9F));
	}

	private static int figureVerticalOrientationsFromChar (int c) {
// we don't want ATE::kBaselineRotatedRomanInVertical for space characters and maybe others.
		return charIsRomanRotateable (c) ? GFXGlyphOrientation.HORIZONTAL_ROTATED : GFXGlyphOrientation.VERTICAL;
	}
}

//----------------------------------------------------------------------
//
//		DispRaw - Base class
//
//----------------------------------------------------------------------
abstract class DispRaw {
	private final PosnStack moPosnStack;
	private TextPosnBase mpoCurrent;
	private TextStream mpoStopStream;
	private int mnStopIndex;
	private boolean mbContinue;
	private boolean mbIsLastParaLine;
	private boolean mbIsLastLineInStream;

	DispRaw (FormatInfo oFormatInfo, PosnStack oPosnStack) {
		moPosnStack = oPosnStack;
		mpoCurrent = moPosnStack.top();
		mpoCurrent.position (TextPosn.POSN_BEFORE);

		if (oFormatInfo.getChange().type() == DispChange.CHANGE_FRAME) {
			mpoStopStream = oFormatInfo.getChange().stream();
			mnStopIndex = oFormatInfo.getChange().index() + oFormatInfo.getChange().count();
		}
	}

	TextPosnBase getPosition () {
		return mpoCurrent;
	}

	boolean canContinue () {
		return mbContinue;
	}

	void run () {
		//int nPushed = 0;
		boolean bDone = false;
		while (! bDone) {
			if (mpoStopStream != null) {
				if ((mpoCurrent.index() >= mnStopIndex) && (mpoCurrent.stream() == mpoStopStream)) {
					return;
				}
			}

			int eItem = mpoCurrent.next (true);
			boolean bAdvanced = false;

			switch (eItem) {
				case TextItem.ATTR:
					bAdvanced = onAttr();
					break;

				case TextItem.CHAR:
					int c = mpoCurrent.nextChar (true);
					int eData = TextCharProp.getCharProperty (c);
					int eBreak = TextCharProp.getBreakClass (eData);

					if ((eBreak == TextCharProp.BREAK_BK)
					 || (eBreak == TextCharProp.BREAK_CR)
					 || (eBreak == TextCharProp.BREAK_LF)
					 || (eBreak == TextCharProp.BREAK_NL)) {
						bDone = true;
						mbContinue = true;
						c = ' ';
						eData = TextCharProp.makeData (TextCharProp.BREAK_SP,
													   TextCharProp.getWidthClass (eData),
													   TextCharProp.GRAPHEME_Default,
													   TextCharProp.WORD_Default,
													   TextCharProp.CASE_Default,
													   TextCharProp.BIDI_WS,
													   TextCharProp.BM_OFF);
					}

					bAdvanced = onChar (c, eData);
					break;

				case TextItem.FIELD:
					TextField poField = mpoCurrent.nextField();
					TextPosnBase oPosn = new TextPosnBase (poField, TextPosn.POSN_BEFORE);
					moPosnStack.push (oPosn);
					mpoCurrent = moPosnStack.top();
					onStreamStart();
					bAdvanced = true; // don't skip first item in stream
					break;

				case TextItem.OBJECT:
					bAdvanced = onObject();
					break;

				case TextItem.PARA:
					bAdvanced = onPara();
					mbIsLastParaLine = true;
					bDone = true;
					mbContinue = true;
					break;

				case TextItem.UNKNOWN:	// end of stream
					assert (moPosnStack.size() > 0);
					moPosnStack.pop();
					if (moPosnStack.size() == 0) {
						mbIsLastParaLine = true;
						mbIsLastLineInStream = true;
						bDone = true;
					} else {
						mpoCurrent = moPosnStack.top();
						bAdvanced = true; // skipped over field ref when we found the field
						onStreamEnd();
					}
					break;
			}

			if (! bAdvanced) {
				mpoCurrent.next();
			}
		}
	}

	boolean isLastParaLine () {
		return mbIsLastParaLine;
	}

	boolean isLastLineInStream () {
		return mbIsLastLineInStream;
	}

	protected boolean onAttr () {
		return false;
	}

	abstract protected boolean onChar (int c, int eData);

	abstract protected boolean onObject ();

	abstract protected boolean onPara ();

	protected void onStreamStart () {
	}

	protected void onStreamEnd () {
	}
}

//----------------------------------------------------------------------
//
//		DispRawCounter - Count the number of characters to
//		allocate
//
//----------------------------------------------------------------------
class RawCounter extends DispRaw {
	private int mnCount;

	RawCounter (FormatInfo oFormatInfo, PosnStack oPosnStack) {
		super (oFormatInfo, oPosnStack);
		mnCount = 0;
	}

	int getCount () {
		return mnCount;
	}

	protected boolean onChar (int c, int eData) {
		mnCount++;
		return false;
	}

	protected boolean onObject () {
		mnCount++;
		return false;
	}

	protected boolean onPara () {
		mnCount++;
		return false;
	}
}

//----------------------------------------------------------------------
//
//		DispRawBuilder - Build the raw line
//
//----------------------------------------------------------------------
class RawBuilder extends DispRaw {
	private final RawProcessor moProcessor;

	RawBuilder (FormatInfo oFormatInfo, DispLineRaw poLine) {
		super (oFormatInfo, oFormatInfo.posnStack());
		moProcessor = new RawProcessor (oFormatInfo, poLine, getPosition(), oFormatInfo.resolveAttr (oFormatInfo.getAttr()));
	}

	void finish () {
		moProcessor.finish (isLastParaLine(), isLastLineInStream());
	}

	protected boolean onAttr () {
		moProcessor.processAttr (getPosition().nextAttr());
		return true;
	}

	protected boolean onChar (int c, int eBreak) {
		moProcessor.processChar (c, eBreak, getPosition());
		return false;
	}

	protected boolean onObject () {
		moProcessor.processObject (getPosition().nextEmbed(), getPosition());
		return true;
	}

	protected boolean onPara () {
		moProcessor.processPara (getPosition());
		return false;
	}

	protected void onStreamStart () {
		moProcessor.processStreamStart (getPosition());
	}

	protected void onStreamEnd () {
		moProcessor.processStreamEnd (getPosition());
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy