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

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

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
package com.adobe.xfa.text;

import com.adobe.xfa.font.FontInstance;

import com.adobe.xfa.gfx.GFXDriver;
import com.adobe.xfa.gfx.GFXEnv;
import com.adobe.xfa.gfx.GFXMapping;
import com.adobe.xfa.gfx.GFXMappingList;

import com.adobe.xfa.ut.CoordPair;
import com.adobe.xfa.ut.Rect;
import com.adobe.xfa.ut.UnitSpan;
import com.adobe.xfa.ut.UniCharIterator;

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

class LayoutRenderer extends GFXEnv {
	private final LayoutDriver mpoDriver;

	LayoutRenderer (TextLayout poLayout, DispLineWrapped poDispLine, UnitSpan oFullAscent, UnitSpan oFullDescent, boolean bAllowCharGlyphs) {
		interactive (false);
		mpoDriver = new LayoutDriver (this, poLayout, poDispLine, oFullAscent, oFullDescent, bAllowCharGlyphs);
		fontService (poDispLine.stream().fontService());
		driverAttach();
	}

	void finish () {
		mpoDriver.finish();
	}

	public GFXDriver driver () {
		return mpoDriver;
	}
}

class LayoutDriver extends GFXDriver {
	static class RunInfo {
		int mGlyphCount;
		CoordPair mDefaultOffset;
		boolean mIsRTL;

		RunInfo (UnitSpan initialY) {
			mGlyphCount = 0;
			mDefaultOffset = new CoordPair (UnitSpan.ZERO, initialY);
			mIsRTL = false;
		}
	};

	private static abstract class RunHelper {
		static class GlyphInfo {
			final int mGlyphID;
			final float mGlyphWidth;

			GlyphInfo (int glyphID, float glyphWidth) {
				mGlyphID = glyphID;
				mGlyphWidth = glyphWidth;
			}
		};

		private final TextAttr mpoAttr;
		private final boolean mbCharMode;
		private final FontInstance moFontInstance;

		void generateRun (CoordPair oPenPosn, TextLayoutLine oLayoutLine, RunInfo runInfo) {
			TextGlyphRun oRun = new TextGlyphRun();

			UnitSpan oVerticalShiftOffset = UnitSpan.ZERO;
			if (mpoAttr != null) {
				TextAttr poAttr = mpoAttr.getOriginalAttr();
				if (poAttr == null) {
					poAttr = mpoAttr;
				} else {
					oVerticalShiftOffset = mpoAttr.baselineShift().getLength();
				}
				oRun.setAttr (poAttr);
			}

			oRun.setCharRun (mbCharMode);
			oRun.setRTL (runInfo.mIsRTL);

			int nGlyphCount = 0;
			float oAccumulatedWidth = 0;

			GlyphInfo glyphInfo = getNextGlyph();
			while (glyphInfo != null) {
				oRun.addGlyph (glyphInfo.mGlyphID);
				oAccumulatedWidth += glyphInfo.mGlyphWidth;
				glyphInfo = getNextGlyph();
				nGlyphCount++;
			}

			CoordPair oRunPosn = new CoordPair (oPenPosn.x(), oPenPosn.y().subtract (oVerticalShiftOffset));
			oRun.setPosition (oRunPosn);
//			if ((runInfo.mGlyphCount == 0) && (oPenPosn.x().value() != 0)) {
			if (! oRunPosn.equals (runInfo.mDefaultOffset)) {		// TODO: need glyph widths to check for shift
				oRun.setShift (oRunPosn.subtract (runInfo.mDefaultOffset));
			}
			runInfo.mDefaultOffset = new CoordPair (oRunPosn.x().add (Units.toUnitSpan (oAccumulatedWidth)),
													runInfo.mDefaultOffset.y());

			oLayoutLine.addRun (oRun);

			runInfo.mGlyphCount += nGlyphCount;
		}

		protected RunHelper (TextAttr poAttr, boolean bCharMode) {
			mpoAttr = poAttr;
			mbCharMode = bCharMode;
			moFontInstance = poAttr != null ? poAttr.fontInstance() : null;
		}

		protected FontInstance getFontInstance () {
			return moFontInstance;
		}

		abstract protected GlyphInfo getNextGlyph ();
	}

	private static class CharRunHelper extends RunHelper {
		private final UniCharIterator mIterator;

		CharRunHelper (TextAttr poAttr, String sText, boolean bCharMode) {
			super (poAttr, bCharMode);
			mIterator = new UniCharIterator (sText);
		}

		CharRunHelper (TextAttr poAttr, String sText) {
			this (poAttr, sText, true);
		}

		protected GlyphInfo getNextGlyph () {
			int c = mIterator.next();
			if (c == 0) {
				return null;
			}

			float oWidth;
			if (getFontInstance() == null) {
				oWidth = 0;
			} else {
				oWidth = getFontInstance().getCharWidth (c, true);
			}

			return new GlyphInfo (c, oWidth);
		}
	}

	private static class CharGlyphRunHelper extends CharRunHelper {
		CharGlyphRunHelper (TextAttr poAttr, String sText) {
			super (poAttr, sText, false);
		}

		protected GlyphInfo getNextGlyph () {
			GlyphInfo info = super.getNextGlyph();
			if (info == null) {
				return null;
			}

			if (getFontInstance() != null) {
				int nGlyphID = getFontInstance().getGlyphID (info.mGlyphID);
				if (nGlyphID != 0) {											// TODO: proper .notdef handling
					info = new GlyphInfo (nGlyphID, info.mGlyphWidth);
				}
			}

			return info;
		}
	}

	private static class GlyphRunHelper extends RunHelper {
		private final int[] mpnGlyphIDs;
		private final int mnLength;
		private int mnIndex;

		GlyphRunHelper (TextAttr poAttr, int[] pnGlyphIDs, int nLength) {
			super (poAttr, false);
			mpnGlyphIDs = pnGlyphIDs;
			mnLength = nLength;
			mnIndex = 0;
		}

		protected GlyphInfo getNextGlyph () {
			if (mnIndex >= mnLength) {
				return null;
			}

			int nGlyph = mpnGlyphIDs[mnIndex];
			mnIndex++;

			float oWidth = 0;
			if (getFontInstance() != null) {
				oWidth = getFontInstance().getGlyphWidth (nGlyph, true); // TBD: vertical text
			}

			return new GlyphInfo (nGlyph, oWidth);
		}
	}

	private final TextLayout mpoLayout;
	private final DispLineWrapped mpoDispLine;
	private final boolean mbAllowCharGlyphs;
	private final TextLayoutLine moLayoutLine = new TextLayoutLine();
	private final GFXMappingList moMappings = new GFXMappingList();
	private TextAttr mpoTextAttr;
	private final RunInfo mRunInfo;
	private final MultiMapper moMultiple = new MultiMapper();

	LayoutDriver (LayoutRenderer oRenderer, TextLayout poLayout, DispLineWrapped poDispLine, UnitSpan oFullAscent, UnitSpan oFullDescent, boolean bAllowCharGlyphs) {
		super (oRenderer);
		mpoLayout = poLayout;
		mpoDispLine = poDispLine;
		mbAllowCharGlyphs = bAllowCharGlyphs;
		mpoTextAttr = null;
		mRunInfo = new RunInfo (poDispLine.getXYOrigin().y().add (oFullAscent));
		initDeviceUnitsPerInch (1000, 1000);
		moLayoutLine.setFullAscent (oFullAscent);
		moLayoutLine.setFullDescent (oFullDescent);

		TextFrame poFrame = poDispLine.frame();
		TextSparseStream poStream = poFrame.getStream();
		int nIndex = poLayout.getLineCount();

		int eStartState;
		if ((nIndex == 0) && (poFrame == poStream.getFrame (0))) {
			eStartState = TextLayoutLine.LINE_END_ULTIMATE;
		} else if (poDispLine.isFirstParaLine()) {
			eStartState = TextLayoutLine.LINE_END_PARA;
		} else {
			eStartState = TextLayoutLine.LINE_END_WORD_WRAP;
		}
		eStartState = ResolveLineEndState (eStartState, poDispLine.getStartBreak());
		moLayoutLine.setLineStartState (eStartState);

		int eEndState;
		if ((nIndex + 1 == poFrame.getLineCount()) && (poFrame == poStream.getFrame (poStream.getFrameCount() - 1))) {
			eEndState = TextLayoutLine.LINE_END_ULTIMATE;
		} else {
			switch (poDispLine.getLastParaLine()) {
				case DispLine.HARD_NEW_LINE:
					eEndState = TextLayoutLine.LINE_END_FORCED;
					break;
				case DispLine.REAL_LAST_LINE:
					eEndState = TextLayoutLine.LINE_END_PARA;
					break;
				default:
					eEndState = TextLayoutLine.LINE_END_WORD_WRAP;
					break;
			}
		}
		eEndState = ResolveLineEndState (eEndState, poDispLine.getEndBreak());
		moLayoutLine.setLineEndState (eEndState);
	}

	void finish () {
		flushMultiple();
		moLayoutLine.setMappings (moMappings);
		mpoLayout.addLine (moLayoutLine);
	}

// Inherited from class GFXDriver
	public void absLine (CoordPair oEnd, int eDrawMode) {
	}

	public void absFillRect (Rect oRect, int eDrawMode) {
	}

// TBD:
	public void paraHint () {
	}

	public void absText (String sText, int eDrawMode) {
		if (sText.length() == 0) {
			return;
		}

		if (mbAllowCharGlyphs) {
			CharRunHelper oHelper = new CharRunHelper (mpoTextAttr, sText);				// TODO: cache and reuse instance?
			oHelper.generateRun (getAbsPosition(), moLayoutLine, mRunInfo);				// TBD: rel position?
		} else {
			CharGlyphRunHelper oHelper = new CharGlyphRunHelper (mpoTextAttr, sText);	// TODO: cache and reuse instance?
			oHelper.generateRun (getAbsPosition(), moLayoutLine, mRunInfo);				// TBD: rel position?
		}
	}

	public void absGlyphs (int[] pnGlyphs, int length) {
		if (length == 0) {
			return;
		}

		GlyphRunHelper oHelper = new GlyphRunHelper (mpoTextAttr, pnGlyphs, length);	// TODO: cache and reuse instance?
		oHelper.generateRun (getAbsPosition(), moLayoutLine, mRunInfo);					// TBD: rel position?

		mRunInfo.mIsRTL = false;
	}

	public void setUnicodeChars (int pcText[]) {										// TODO: add render context support in C++ for leaders in Editor?
		StringBuilder sContent = new StringBuilder();
		int nSize = pcText.length;
		for (int i = 0; i < nSize; i++) {
			int c = pcText[i];
			if ((i + 1) == nSize) {
				switch (moLayoutLine.getLineEndState()) {
					case TextLayoutLine.LINE_END_FORCED:
						assert (c == ' ');
						c = '\n';
						break;
					case TextLayoutLine.LINE_END_PARA:
						assert (c == ' ');
						c = '\u2029';
						break;
				}
			}
			sContent.append ((char) c);
		}
		moLayoutLine.setContent (sContent.toString());
	}

	public void mapGlyphs (GFXMappingList oMappings, boolean bIsRTL) {
		boolean bCharIndexFound = false;

		for (int i = 0; i < oMappings.getMappingCount(); i++) {
			GFXMapping oRelMapping = oMappings.getMapping (i);

			if ((! bCharIndexFound) && (oRelMapping.getCharCount() > 0)) {
				int nCharIndex = oRelMapping.getCharIndex (0);
				TextAttr poAttr = mpoDispLine.getMappedAttr (nCharIndex);
				if (poAttr != null) {
					mpoTextAttr = poAttr;
				}
				bCharIndexFound = true;
			}

			int eMultiple = moMultiple.canAccumulate (oRelMapping, mRunInfo.mGlyphCount);

			if (eMultiple != MultiMapper.GLYPH_NOT_MULTIPLE) {
				if (eMultiple == MultiMapper.GLYPH_FLUSH_FIRST) {
					flushMultiple();
				}
				moMultiple.accumulate (oRelMapping, mRunInfo.mGlyphCount);
			}

			else {
				flushMultiple();
				GFXMapping oAbsMapping = new GFXMapping();
				int j;
				for (j = 0; j < oRelMapping.getCharCount(); j++) {
					oAbsMapping.addCharIndex (oRelMapping.getCharIndex (j));
				}
				for (j = 0; j < oRelMapping.getGlyphCount(); j++) {
					oAbsMapping.addGlyphIndex (oRelMapping.getGlyphIndex (j) + mRunInfo.mGlyphCount);
				}
				moMappings.addMapping (oAbsMapping);
			}
		}

		mRunInfo.mIsRTL = bIsRTL;
	}

	public int getMappingLevel () {
		return MAPPING_FULL;
	}

	public int height () {
		return 1 << 16;
	}

	public int width () {
		return 1 << 16;
	}

	public boolean interactive () {
		return false;
	}

	public void setClipRect (Rect oRect) {
	}

	private void flushMultiple () {
		moMultiple.flush (moMappings);
	}

	private static int ResolveLineEndState (int eLineState, int eBreakState) {
		if (eLineState == TextLayoutLine.LINE_END_WORD_WRAP) {
			switch (eBreakState) {
				case DispLine.LINE_BREAK_HYPHEN_SUPPRESS:
					return TextLayoutLine.LINE_END_LAST_WORD;
				case DispLine.LINE_BREAK_HYPHEN:
					return TextLayoutLine.LINE_END_HYPHEN;
				case DispLine.LINE_BREAK_FORCED:
					return TextLayoutLine.LINE_END_EMERGENCY;
			}
		}

		return eLineState;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy