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

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

The newest version!
package com.adobe.xfa.text;

import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

import com.adobe.xfa.font.FontItem;
import com.adobe.xfa.gfx.GFXColour;
import com.adobe.xfa.gfx.GFXDriver;
import com.adobe.xfa.gfx.GFXFillAttr;
import com.adobe.xfa.gfx.GFXGlyphOrientation;
import com.adobe.xfa.gfx.GFXLineAttr;
import com.adobe.xfa.gfx.GFXTextAttr;
import com.adobe.xfa.font.FontInstance;
import com.adobe.xfa.text.Decoration.DecorationKey;
import com.adobe.xfa.text.Decoration.DecorationValue;
import com.adobe.xfa.ut.CoordPair;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.Rect;
import com.adobe.xfa.ut.UnitSpan;

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

//----------------------------------------------------------------------
//
//		DrawAttr - Class declaration
//
//----------------------------------------------------------------------
class DrawAttr {

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

	static class ClipRect {
		private final DrawParm moParm;
		private boolean mbClipRectSet;

		ClipRect (DrawParm oParm) {
			moParm = oParm;
			mbClipRectSet = false;
		}

		void detach () {							// TODO: need explicit calls to detach clip rect
			if (mbClipRectSet) {
				moParm.driver().popClipRect();
				mbClipRectSet = false;
			}
		}

		void set (Rect oClipRect) {
			if (mbClipRectSet) {
				moParm.driver().popClipRect();
			}

			moParm.driver().pushClipRect (true, oClipRect);
			mbClipRectSet = true;
		}
	}

	private static class DrawData extends GFXTextAttr {
		DispRun mRun;
		TextAttr mTextAttr;

		DrawData () {
		}

		DrawData (GFXTextAttr source) {
			super (source);
		}
		
		public boolean equals(Object object) { // NOPMD - UselessOverridingMethod - here for documentation/FindBugs suppression
			// The fields added in this derived class do not participate in equality
			return super.equals(object);
		}
		
		public int hashCode() { // NOPMD - UselessOverridingMethod - here for documentation/FindBugs suppression
			// The fields added in this derived class do not participate in equality
			return super.hashCode();
		}
	}

	private final DispLineWrapped mpoLine;
	private final DrawParm moParm;
	private final DrawSelection moSelection = new DrawSelection();
	private final boolean mbInteractive;
	private final boolean mbBreakOnGlyphs;

	private UnitSpan moBaseline;
	private TextAttr mpoAttr;
	private final GFXTextAttr moGfxAttr = new GFXTextAttr();
	private FontInstance moFontOverride;

	private UnitSpan moCharSpacing = UnitSpan.ZERO;
	private UnitSpan moWordSpacing = UnitSpan.ZERO;
	private boolean mbCharSpacingChanged;
	private boolean mbWordSpacingChanged;

	private boolean mbRTL;

	private final Decoration moUnderline;
	private final Decoration moLineThrough;
	private final Decoration moOverline;
	private static final boolean mbIncludeSpaces = false;
	private final SortedMap moDecorations = new TreeMap();

	private GlyphLoc moGlyphLoc;
	private int mnGlyphLocIndex;
	private int mnClipIndex;
	private int mnBackup;
	private boolean mbBackupSet;
	private int mnPrevCharIndex;

	private Rect moClipRect;
	private boolean mbClipRectSet;

	private int meGlyphOrientation;

	DrawAttr (DispLineWrapped poLine, DrawParm oParm) {
		mpoLine = poLine;
		moParm = oParm;
		moUnderline = Decoration.createUnderline (poLine, oParm);
		moLineThrough = Decoration.createLineThrough (poLine, oParm);
		moOverline = Decoration.createOverline (poLine, oParm);
		mnPrevCharIndex = poLine.getCharCount();	// an impossible index
		mbClipRectSet = false;
		meGlyphOrientation = GFXGlyphOrientation.HORIZONTAL;
		mbInteractive = moParm.env().interactive();
		mbBreakOnGlyphs = moParm.driver().getMappingLevel() == GFXDriver.MAPPING_LEGACY;
	}

	TextAttr getAttr () {
		return mpoAttr;
	}

	FontInstance getWorkingFont () {
		return (moFontOverride == null) ? mpoAttr.fontInstance() : moFontOverride;
	}

	FontInstance getFontOverride () {
		return moFontOverride;
	}

	GFXTextAttr getGfxAttr () {
		return moGfxAttr;
	}

	void drawBackground () {
		TextSelection poPrimary = moParm.primary();
		TextSelection poSecondary = moParm.secondary();
		if ((poPrimary != null) && poPrimary.isEmpty()) {
			poPrimary = null;
		}
		if ((poSecondary != null) && poSecondary.isEmpty()) {
			poSecondary = null;
		}

		if ((mpoLine.hasSingleColour()) && (poPrimary == null) && (poSecondary == null)) {
			GFXColour oFG;
			GFXColour oBG;
			boolean bTransparent;
			TextAttr poAttr = mpoLine.getStartAttr();

			if (poAttr == null) {
				oFG = GFXColour.black();
				oBG = GFXColour.white();
				bTransparent = false;
			} else {
				oFG = poAttr.colour();
				oBG = poAttr.colourBg();
				bTransparent = poAttr.transparent();
			}
			moSelection.addClip (Units.toFloat (mpoLine.getAMin()), Units.toFloat (mpoLine.getAMin().add (mpoLine.getAExtent())), oFG, oBG, bTransparent);
		}

		else {
			mpoLine.preAllocOffsets();
		}

		int i;
		for (i = 0; i < mpoLine.getGlyphCount(); i++) {
			processBackgroundGlyph (i, poPrimary, poSecondary);
		}

		if (moParm.driver() != null) {
			for (i = 0; i < moSelection.getClipCount(); i++) {
				DrawSelection.Clip oClip = moSelection.getClip (i);
				if (! oClip.mbTransparent) {
					float oStart = oClip.moLeft;
					if (oStart < moParm.invalidAMin()) {
						oStart = moParm.invalidAMin();
					}

					float oEnd = oClip.moRight;
					if (oEnd > moParm.invalidAMax()) {
						oEnd = moParm.invalidAMax();
					}

					if (oStart < oEnd) {
						GFXFillAttr oFillAttr = new GFXFillAttr();
						oFillAttr.colour (oClip.moBG);
						oFillAttr.colourBg (oClip.moBG);
						moParm.driver().fillAttr (oFillAttr);
						Rect oFillRect = new Rect (Units.toUnitSpan (oStart), mpoLine.getBMinExtended (true), Units.toUnitSpan (oEnd), mpoLine.getBMaxExtended (true));
						oFillRect = ABXY.toXY (mpoLine.getXYOrigin(), oFillRect, mpoLine.frame().getLayoutOrientation());
						moParm.driver().relFillRect (oFillRect);
					}
				}
			}
		}
	}

	@FindBugsSuppress(code="NP")	// false positive? - oFontInstance
	boolean prepareGlyph (RenderInfo oRenderInfo) {
		oRenderInfo.mpoGlyphLoc = null;

		for (boolean bRetry = true; bRetry; ) {
			oRenderInfo.mnGlyphID = 0;
			oRenderInfo.mcGlyph = '\0';
			oRenderInfo.mbFlushBefore = false;
			oRenderInfo.mbFlushAfter = false;
			oRenderInfo.mbShifted = false;

			oRenderInfo.mpoGlyphLoc = getGlyphLoc (mnGlyphLocIndex);
			if (oRenderInfo.mpoGlyphLoc == null) {
				if (mbInteractive) {
					oRenderInfo.mpoGlyphLoc = nextSelection();
				}
				if (oRenderInfo.mpoGlyphLoc == null) {
					return false;
				}
				oRenderInfo.mbFlushBefore = true;
				oRenderInfo.mbShifted = true;
			}

			DrawSelection.Clip oClip = new DrawSelection.Clip();
			DrawSelection.Clip poOverride = null;

			if (mbInteractive) {
				if (moSelection.getClipCount() == 0) {
					oClip.moFG = GFXColour.black();
					oClip.moBG = GFXColour.white();
				}

				else if (moSelection.getClipCount() == 1) {
					oClip = moSelection.getClip (0);
				}

				else if (moSelection.getClipCount() > 1) {
					GlyphLoc poOldGlyphLoc = oRenderInfo.mpoGlyphLoc;
					float[] extents = getGlyphExtents (oRenderInfo.mpoGlyphLoc);

					if ((mnClipIndex < moSelection.getClipCount()) && (extents[0] >= moSelection.getClip (mnClipIndex).moRight)) {
						oRenderInfo.mpoGlyphLoc = nextSelection();
						if (oRenderInfo.mpoGlyphLoc == null) {
							return false;
						}
						oRenderInfo.mbFlushBefore = true;
						oRenderInfo.mbShifted = true;
					}

					oClip = moSelection.getClip (mnClipIndex);

					if (! mbBackupSet) {
						if (oRenderInfo.mpoGlyphLoc != poOldGlyphLoc) {
							extents = getGlyphExtents (oRenderInfo.mpoGlyphLoc);
						}

						if (extents[1] > oClip.moRight) {
							mnBackup = mnGlyphLocIndex;
							mbBackupSet = true;
						}
					}
				}

				poOverride = oClip;
			}

			mnGlyphLocIndex++;

			DrawData data = initDrawAttr (oRenderInfo.mpoGlyphLoc);

			int nCharIndex = oRenderInfo.mpoGlyphLoc.getMapIndex();
			oRenderInfo.mcGlyph = mpoLine.getChar (nCharIndex);

// Unicode direction control characters appear in the glyph array created
// by WinSoft; don't attempt to draw them.
			if ((oRenderInfo.mcGlyph < 0x202A) || (oRenderInfo.mcGlyph > 0x202E)) {
				if (nCharIndex == mnPrevCharIndex) {
// Multiple glyphs from the same character: force run break so that
// Acrobat can map properly.  TBD: revisit when acrobat uses new mapping
					oRenderInfo.mbFlushBefore = true;
					oRenderInfo.mbFlushAfter = true;
				}
				mnPrevCharIndex = nCharIndex;

				if (poOverride != null) {
					data.colour (poOverride.moFG);
					data.colourBg (poOverride.moBG);
				}

				Glyph oGlyph = mpoLine.getGlyph (oRenderInfo.mpoGlyphLoc.getGlyphIndex());

// Pick up any TextAttr based attributes if the attribute pointer is
// non-null.
				FontInstance oFontInstance = null;
				UnitSpan oNewBaseline = moParm.offsetText();
				UnitSpan oNewCharSpacing = UnitSpan.ZERO;
				UnitSpan oNewWordSpacing = UnitSpan.ZERO;

				mbCharSpacingChanged = false;
				mbWordSpacingChanged = false;

				if (data.mTextAttr != null) {
					oFontInstance = data.mTextAttr.fontInstance();
					if ((! mpoLine.isLayoutLine()) && data.mTextAttr.baselineShiftEnable() && (! data.mTextAttr.baselineShift().isNeutral())) {
						oNewBaseline = data.mTextAttr.baselineShift().applyShift (oNewBaseline, moParm.shiftSize());
					}
					if (data.mTextAttr.charSpacingEnable()) {
						oNewCharSpacing = data.mTextAttr.charSpacing().getLength();
					}
					if (data.mTextAttr.wordSpacingEnable()) {
						oNewWordSpacing = data.mTextAttr.wordSpacing().getLength();
					}
				}

// Baseline change: must flush before
				if (! UnitSpan.match (oNewBaseline, moBaseline)) {
					moBaseline = oNewBaseline;
					oRenderInfo.mbFlushBefore = true;
					oRenderInfo.mbShifted = true;
				}

// Character or word spacing change: if the driver handles these, a
// change must be treated as an attribute change.  Don't worry about run
// breaks here; that happens in the TextDrawRun class.
				if (! UnitSpan.match (oNewCharSpacing, moCharSpacing)) {
					moCharSpacing = oNewCharSpacing;
					mbCharSpacingChanged = true;

					if (moParm.driver().charSpacingSupported()) {
						oRenderInfo.mbAttrChange = true;
						oRenderInfo.mbFlushBefore = true;
						oRenderInfo.mbShifted = true;
					}
				}

				if (! UnitSpan.match (oNewWordSpacing, moWordSpacing)) {
					moWordSpacing = oNewWordSpacing;
					mbWordSpacingChanged = true;

					if (moParm.driver().wordSpacingSupported()) {
						oRenderInfo.mbAttrChange = true;
						oRenderInfo.mbFlushBefore = true;
						oRenderInfo.mbShifted = true;
					}
				}

// Must break runs on direction change.
				boolean bRTL = oGlyph.isRTL();
				if (bRTL != mbRTL) {
					oRenderInfo.mbFlushBefore = true;
					mbRTL = bRTL;
				}

// Check for any other attribute change.
				int eGlyphOrientation = oGlyph.getOrientation();
				if ((mpoAttr == null)
				 || ((oFontInstance != mpoAttr.fontInstance()) && (oFontInstance != null))
				 || ((data.mTextAttr != null) && (data.mTextAttr.transparent() != mpoAttr.transparent()))
				 || (! moGfxAttr.equals (data))
				 || (meGlyphOrientation != eGlyphOrientation)) {
					mpoAttr = data.mTextAttr;
					moGfxAttr.copyFrom (data);
					meGlyphOrientation = eGlyphOrientation;
					oRenderInfo.mbAttrChange = true;
					oRenderInfo.mbFlushBefore = true;
				}

// Pick up the character for rendering.
				// TODO: FindBugs reports possible null pointer dereference of oFontInstance here
				FontItem poFontItem = oFontInstance.getFontItem();
				mpoLine.getRenderChar (data.mTextAttr, oFontInstance, oRenderInfo.mpoGlyphLoc, oGlyph, oRenderInfo, poFontItem);

				if (mbBreakOnGlyphs) {
					if (bRTL || (oRenderInfo.mcGlyph == '\0')) {
						oRenderInfo.mbFlushBefore = true;
						oRenderInfo.mbFlushAfter = true;
					}
				}

// Watson 1260903: Work-around for font service restrictions.  With some
// output targets, glyph-only output must be done with Unicode fonts
// only.  The line signals this by changing the font item pointer to one
// for a Unicode font.	When the font item is overridden in this way,
// there must be a corresponding font instance override to use at render
// time.
// If the font item hasn't changed from the one in the font instance,
// clear any override that might be in effect.
				if ((poFontItem == oFontInstance.getFontItem()) || (mpoAttr == null)) {
					if (moFontOverride != null) {
						moFontOverride = null;
						oRenderInfo.mbAttrChange = true;
						oRenderInfo.mbFlushBefore = true;
					}
				}

// If the font item has changed, the glyph will have to be rendered with
// an override.  This override may already be in effect, so try to limit
// run breaks in such a case.
				else {
					FontInstance oOverride = poFontItem.reconcile (mpoAttr.size());
					if (oOverride != moFontOverride) {
						moFontOverride = oOverride;
						oRenderInfo.mbAttrChange = true;
						oRenderInfo.mbFlushBefore = true;
					}
				}

				bRetry = false;
			}
		}

		return oRenderInfo.mpoGlyphLoc != null;
	}

	UnitSpan getBaseline () {
		return moBaseline;
	}

	boolean assertAttrs (ClipRect oClipRect) {
		GFXDriver poDriver = moParm.driver();
		poDriver.textAttr (moGfxAttr);

		poDriver.glyphOrientation (meGlyphOrientation); // before font change
		if (mpoAttr != null) {
			poDriver.fontInstance (getWorkingFont());

			poDriver.mode (mpoAttr.transparent() ? GFXDriver.MODE_TRANSPARENT : GFXDriver.MODE_OPAQUE);

			if (mbCharSpacingChanged) {
				poDriver.charSpacing (moCharSpacing);
			}
			if (mbWordSpacingChanged) {
				poDriver.wordSpacing (moWordSpacing);
			}
		}

		if (mbClipRectSet) {
			if (moClipRect.isDegenerate()) {
				return false;
			}
			oClipRect.set (moClipRect);
		}

		return true;
	}

	void flush () {
		if (mpoLine.hasDecoration()) {
			moUnderline.complete (moParm, moDecorations, mbIncludeSpaces);
			moLineThrough.complete (moParm, moDecorations, mbIncludeSpaces);
			moOverline.complete (moParm, moDecorations, mbIncludeSpaces);

			Decoration.DecorationKey keys[] = new Decoration.DecorationKey[moDecorations.size()];
			int i = 0;
			for (Decoration.DecorationKey key : moDecorations.keySet())
				keys[i++] = key;
				
			int nDecorationStart = 0;

			for (int nClip = 0; nClip < moSelection.getClipCount(); nClip++) {
				DrawSelection.Clip oClip = moSelection.getClip (nClip);

				for (; ; ) {
					if ((nDecorationStart >= moDecorations.size())
					 || (keys[nDecorationStart].moXEnd > oClip.moLeft)) {
						break;
					}
					nDecorationStart++;
				}
				if (nDecorationStart >= moDecorations.size()) {
					break;
				}

				for (int j = nDecorationStart; j < moDecorations.size(); j++) {
					Decoration.DecorationKey oKey = keys[j];
					if (oKey.moXStart >= oClip.moRight) {
						break;
					}
					Decoration.DecorationValue oValue = moDecorations.get (oKey);

					float oLeft = oClip.moLeft;
					if (oKey.moXStart > oLeft) {
						oLeft = oKey.moXStart;
					}
					float oRight = oClip.moRight;
					if (oKey.moXEnd < oRight) {
						oRight = oKey.moXEnd;
					}

					GFXLineAttr oLineAttr = new GFXLineAttr();
					oLineAttr.colour (oClip.moFG);
					oLineAttr.colourBg (oClip.moBG);
					oLineAttr.width (oValue.moWidth);

					CoordPair oP1 = new CoordPair (Units.toUnitSpan (oLeft), oValue.moYOffset);
					CoordPair oP2 = new CoordPair (Units.toUnitSpan (oRight), oValue.moYOffset);
					moParm.driver().lineAttr (oLineAttr);
					oP1 = ABXY.toXY (mpoLine.getXYOrigin(), oP1, mpoLine.frame().getLayoutOrientation());
					oP2 = ABXY.toXY (mpoLine.getXYOrigin(), oP2, mpoLine.frame().getLayoutOrientation());
					moParm.driver().relLine (oP1, oP2);
				}
			}
		}
	}

	void reset (DrawAttr oSource) {
		mpoAttr = oSource.mpoAttr;
		moGfxAttr.copyFrom (oSource.moGfxAttr);
		moFontOverride = oSource.moFontOverride;

		moCharSpacing = oSource.moCharSpacing;
		mbCharSpacingChanged = oSource.mbCharSpacingChanged;
		moWordSpacing = oSource.moWordSpacing;
		mbWordSpacingChanged = oSource.mbWordSpacingChanged;

		moClipRect = oSource.moClipRect;
		mbClipRectSet = oSource.mbClipRectSet;
		meGlyphOrientation = oSource.meGlyphOrientation;
	}

	static boolean getSelectionRectangles (DispLineWrapped poLine, TextSelection poSelection, UnitSpan oYOffset, List oRectangles) {
		TextStream poStream = poSelection.stream();
		int nPositions = poLine.getPositionCount();
		boolean bFound = false;
		int nSelectionStart = poSelection.start().index();
		int nSelectionEnd = poSelection.end().index();
		int i;

		for (i = 0; i < nPositions; i++) {
			DispPosn oPosition = poLine.getPosition (i);
			if ((oPosition.pp().stream() == poStream) && (nSelectionStart < oPosition.pp().index() + oPosition.getMapLength()) && (nSelectionEnd > oPosition.pp().index())) {
				bFound = true;
				break;
			}
		}
		if (! bFound) {
			return false;
		}

		DrawSelection oSelectionInfo = new DrawSelection();

		for (i = 0; i < poLine.getGlyphCount(); i++) {
			processBackgroundGlyph (poLine, poLine.getGlyph (i), poLine.getOrderedGlyphLoc (i), poSelection, null, GFXColour.black(), GFXColour.white(), false, oSelectionInfo);
		}

		bFound = false;
		for (i = 0; i < oSelectionInfo.getClipCount(); i++) {
			DrawSelection.Clip oClip = oSelectionInfo.getClip (i);
			if ((oClip.moFG != GFXColour.black()) && (oClip.moBG != GFXColour.white())) {
				Rect oArea = new Rect (Units.toUnitSpan (oClip.moLeft),
									   oYOffset,
									   Units.toUnitSpan (oClip.moRight),
									   poLine.getBExtent().add (oYOffset));
				oArea = ABXY.toXY (poLine.getXYOrigin(), oArea, poLine.frame().getLayoutOrientation());
				oRectangles.add (oArea);
				bFound = true;
			}
		}

		return bFound;
	}

	private void processBackgroundGlyph (int nGlyphIndex, TextSelection poPrimary, TextSelection poSecondary) {
		Glyph oGlyph = mpoLine.getGlyph (nGlyphIndex);
		GlyphLoc oGlyphLoc = mpoLine.getOrderedGlyphLoc (nGlyphIndex);
		DrawData data = initDrawAttr (oGlyphLoc);

		boolean bAnySelection = (poPrimary != null) || (poSecondary != null);

		if ((! mpoLine.hasSingleColour()) || bAnySelection) {
			processBackgroundGlyph (mpoLine, oGlyph, oGlyphLoc, poPrimary, poSecondary, data.mTextAttr.colour(), data.mTextAttr.colourBg(), /* (data.mTextAttr == null) ? false : */ data.mTextAttr.transparent(), moSelection);
		}

		if (mpoLine.hasDecoration()) {
			if ((oGlyphLoc.getMapLength() > 1) || mpoLine.isVisualChar (oGlyphLoc.getMapIndex())) {
				FontInstance poFontInstance = null;
				FontInstance poOldFontInstance = null;

				if (mpoAttr != null) {
					poOldFontInstance = mpoAttr.fontInstance();
				}

				if (data.mTextAttr != null) {
					poFontInstance = data.mTextAttr.fontInstance();
				}

				if (poFontInstance != poOldFontInstance) {
					if ((poOldFontInstance == null)
					 || (poFontInstance == null)
					 || (poFontInstance != poOldFontInstance)) {
						moUnderline.fontChange (moParm, moDecorations);
						moLineThrough.fontChange (moParm, moDecorations);
						moOverline.fontChange (moParm, moDecorations);
						mpoAttr = data.mTextAttr;
					}
				}

				int nFlags = 0;

				if (GFXGlyphOrientation.usesVerticalGlyphs (data.mRun.glyphOrientation())) {
					nFlags |= Decoration.DECORATION_VERTICAL;
				}

				if ((oGlyphLoc.getMapLength() == 1)
				 && (mpoLine.getBreakClass (oGlyphLoc.getMapIndex()) == TextCharProp.BREAK_SP)) {
					nFlags |= Decoration.DECORATION_SPACE;
				}

				float oLeft = oGlyph.getDrawX (mpoLine) + mpoLine.getAMinFloat();
				float oRight = oGlyph.getDrawNextX (mpoLine) + mpoLine.getAMinFloat();

// If there is a truncation rectangle, and this glyph extends beyond it,
// turn off the decoration line.
				if (moParm.truncate() != null) {
					if (oLeft < moParm.truncateAMin()) {		// extends beyond the left margin
						nFlags |= Decoration.DECORATION_SUPPRESS;
					} else {
						float oOverflow = oRight - moParm.truncateAMax();
						if (oOverflow > 0) {
							if ((oGlyph.isRTL())
							 || ((oGlyphLoc.getMapIndex() + 1) != mpoLine.getVisualCharCount())
							 || (oOverflow > 0.5f)) {
								nFlags |= Decoration.DECORATION_SUPPRESS;
							}
						}
					}
				} else if ((oLeft > moParm.invalidAMax()) || (oRight < moParm.invalidAMin())) {
					nFlags |= Decoration.DECORATION_SUPPRESS;
				}

				moUnderline.update (moParm, data.mTextAttr, oLeft, oRight, nFlags, moDecorations);
				moLineThrough.update (moParm, data.mTextAttr, oLeft, oRight, nFlags, moDecorations);
				moOverline.update (moParm, data.mTextAttr, oLeft, oRight, nFlags, moDecorations);
			}
		}
	}

	private static void processBackgroundGlyph (DispLineWrapped poLine, Glyph oGlyph, GlyphLoc oGlyphLoc, TextSelection poPrimary, TextSelection poSecondary, GFXColour oFG, GFXColour oBG, boolean bTransparent, DrawSelection oSelection) {
		int nBaseIndex = oGlyphLoc.getMapIndex();
		boolean bAnySelection = (poPrimary != null) || (poSecondary != null);
		DispLine.PosnInfo posnInfo = new DispLine.PosnInfo();

		for (int i = 0; i < oGlyphLoc.getMapLength(); i++) {
			int nCharIndex;
			if (oGlyph.isRTL()) {
				nCharIndex = nBaseIndex + oGlyphLoc.getMapLength() - 1 - i;
			} else {
				nCharIndex = nBaseIndex + i;
			}

			int nPositionIndex = poLine.getPositionMap().findItem (nCharIndex);
			assert (poLine.getPositionMap().isValidMapIndex (nPositionIndex));
			DispPosn oPosition = poLine.getPosition (nPositionIndex);

			TextStream poSelStream = oPosition.pp().stream();
			DispLine.charToStreamInfo (oPosition, nCharIndex, posnInfo);

			for (int nSel = 0; nSel < posnInfo.auxInfo; nSel++) {
				TextSelection poSelection = null;
				TextStream poStream = poSelStream;
				int nStreamIndex = posnInfo.index + nSel;

				if (bAnySelection) {
					while (poStream != null) {
						if (poPrimary != null) {
							if ((poPrimary.stream() == poStream) && (nStreamIndex >= poPrimary.start().index()) && (nStreamIndex < poPrimary.end().index())) {
								poSelection = poPrimary;
								break;
							}
						}
						if (poSecondary != null) {
							if ((poSecondary.stream() == poStream) && (nStreamIndex >= poSecondary.start().index()) && (nStreamIndex < poSecondary.end().index())) {
								poSelection = poSecondary;
								break;
							}
						}

						TextPosn poParentPosition = poStream.position();
						if (poParentPosition == null) {
							poStream = null;
						} else {
							poStream = poParentPosition.stream();
							nStreamIndex = poParentPosition.index();
						}
					}
				}

				DispLineWrapped.CharRange oRange = poLine.getCharExtent (nPositionIndex, nCharIndex, nSel);
				if (poSelection != null) {
					oSelection.addClip (oRange.moMinX, oRange.moMaxX, poSelection.colour(), poSelection.colourBg(), false);
				} else {
					oSelection.addClip (oRange.moMinX, oRange.moMaxX, oFG, oBG, bTransparent);
				}
			}
		}
	}

	private DrawData initDrawAttr (GlyphLoc oGlyphLoc) {
		int nCharIndex = oGlyphLoc.getMapIndex();
		DispRun run = mpoLine.getMappedRun (nCharIndex);
		TextAttr textAttr = run.getAttr();
		DrawData data;

		if (textAttr == null) {
			data = new DrawData();
		} else {
			data = new DrawData (textAttr.gfxTextAttr());
		}

		data.mRun = run;
		data.mTextAttr = textAttr;

		return data;
	}

	private GlyphLoc nextSelection () {
		mnClipIndex++;
		if (mnClipIndex >= moSelection.getClipCount()) {
			return null;
		}

		if (mbBackupSet) {
			mnGlyphLocIndex = mnBackup;
			mbBackupSet = false;
		}
		setClipRect();

		return getGlyphLoc (mnGlyphLocIndex);
	}

	private GlyphLoc getGlyphLoc (int nGlyphLocIndex) {
		if (nGlyphLocIndex < mpoLine.getGlyphCount()) {
			moGlyphLoc = mpoLine.getOrderedGlyphLoc (nGlyphLocIndex);
			return moGlyphLoc;
		}

		return null;
	}

	private void setClipRect () {
		DrawSelection.Clip oClip = moSelection.getClip (mnClipIndex);
//		UnitSpan oRight = Units.toUnitSpan (oClip.moRight);

// Watson 1102681: Any glyph may draw outside its computed clip area, and
// will be picked up by the next clip area.  However, the there is no
// next clip area for the right-most one, so extend its right side to the
// stream extent.
		
		// JavaPort: This code has no effect - is this another bug in the C++?
//		if ((mnClipIndex + 1) >= moSelection.getClipCount()) {
//			UnitSpan oMaxRight = mpoLine.frame().getExtent().right();
//			if (oRight.lt (oMaxRight)) {
//				oRight = oMaxRight;
//			}
//		}

		moClipRect = new Rect (Units.toUnitSpan (oClip.moLeft),
							   mpoLine.getBMinExtended (true),
							   Units.toUnitSpan (oClip.moRight),
							   mpoLine.getBMaxExtended (true));
		moClipRect = ABXY.toXY (mpoLine.getXYOrigin(), moClipRect, mpoLine.frame().getLayoutOrientation());

		mbClipRectSet = true;
	}

	private float[] getGlyphExtents (GlyphLoc poGlyphLoc) {
		int nGlyphIndex = poGlyphLoc.getGlyphIndex();
		Glyph oGlyph = mpoLine.getGlyph (nGlyphIndex);

		DispRect oExtent = mpoLine.getABGlyphBBox (nGlyphIndex, true);
		float oXOffset = oGlyph.getDrawX (mpoLine) + mpoLine.getAMinFloat();

		oExtent.xMin += oXOffset;
		oExtent.xMax += oXOffset;

		float oLeft = oGlyph.getDrawX (mpoLine) + mpoLine.getAMinFloat();
		if (oLeft > oExtent.xMin) {
			oLeft = oExtent.xMin;
		}
		float oRight = oGlyph.getDrawNextX (mpoLine) + mpoLine.getAMinFloat();
		if (oRight < oExtent.xMax) {
			oRight = oExtent.xMax;
		}

		float[] result = new float [2];
		result[0] = oLeft;
		result[1] = oRight;
		return result;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy