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

com.adobe.xfa.layout.BoxModelContent Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 * ADOBE CONFIDENTIAL
 *
 * Copyright 2007 Adobe Systems Incorporated All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property of
 * Adobe Systems Incorporated and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Adobe Systems
 * Incorporated and its suppliers and may be covered by U.S. and Foreign
 * Patents, patents in process, and are protected by trade secret or copyright
 * law. Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained from
 * Adobe Systems Incorporated.
 */
package com.adobe.xfa.layout;

//import com.adobe.xfa.Bool;
import com.adobe.xfa.Element;
import com.adobe.xfa.EnumAttr;
//import com.adobe.xfa.EnumValue;
import com.adobe.xfa.AppModel;
import com.adobe.xfa.Int;
import com.adobe.xfa.Measurement;
import com.adobe.xfa.STRS;
import com.adobe.xfa.XFA;
import com.adobe.xfa.content.Content;
//import com.adobe.xfa.content.DateTimeValue;
//import com.adobe.xfa.content.DateValue;
//import com.adobe.xfa.content.DecimalValue;
import com.adobe.xfa.content.ExDataValue;
//import com.adobe.xfa.content.FloatValue;
//import com.adobe.xfa.content.IntegerValue;
//import com.adobe.xfa.content.TextValue;
//import com.adobe.xfa.content.TimeValue;
import com.adobe.xfa.template.TemplateResolver;
import com.adobe.xfa.template.containers.Draw;
import com.adobe.xfa.template.containers.Field;
import com.adobe.xfa.template.formatting.Picture;
import com.adobe.xfa.template.ui.UI;
import com.adobe.xfa.text.TextAttr;
import com.adobe.xfa.text.TextDisplay;
import com.adobe.xfa.text.TextMeasurement;
import com.adobe.xfa.text.TextPosn;
import com.adobe.xfa.text.TextPosnBase;
import com.adobe.xfa.text.TextRange;
import com.adobe.xfa.text.TextRegion;
import com.adobe.xfa.ut.CoordPair;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.LcLocale;
import com.adobe.xfa.ut.LcNum;
import com.adobe.xfa.ut.Margins;
import com.adobe.xfa.ut.ObjectHolder;
import com.adobe.xfa.ut.Rect;
import com.adobe.xfa.ut.ResId;
import com.adobe.xfa.ut.PictureFmt;
import com.adobe.xfa.ut.IntegerHolder;
import com.adobe.xfa.ut.StringUtils;
import com.adobe.xfa.ut.UnitSpan;

import java.util.List;


/**
 * This class provides the implementation for box model formatting
 * of a  nodes with textual content and caption regions
 *
 * @exclude from published api.
 */
class BoxModelContent extends BoxModelCaptionable {

	//	protected AXTEDisplay m_oContentDisplay;
	protected Rect m_oContentDisplayExtent = Rect.ZERO;
	protected boolean m_bSplitContent;	
	protected TextPosn m_oStartContentPosn = new TextPosn();
	protected TextPosn m_oEndContentPosn = new TextPosn();
	protected TextAttr	m_oAmbientTextAttr = new TextAttr();
	
	//Is there embedded content within this box model
	protected boolean	m_bEmbeddedContent;

	// Flag to indicate whether we need to keep the text stream from being destroyed in re-initialization
  	protected boolean	m_bKeepContentTextStream;  	

	// Force to grow W/H
	protected boolean	m_bForcedGrowableW;	
	protected boolean	m_bForcedGrowableH;

	public BoxModelContent(LayoutEnv oEnv, boolean bAllowRenderProxy) {
		super(oEnv, bAllowRenderProxy);
		m_bEmbeddedContent = false;
		m_bSplitContent = false ;
		m_bKeepContentTextStream = false;
		m_bForcedGrowableW = false;
		m_bForcedGrowableH = false;
	}

	public void clear() {
		//
		//Empty the box model
		//
	// Javaport: not yet available. TODO
	//	m_oContentDisplay.setNull();
		m_bKeepContentTextStream = false;
		m_bSplitContent = false;

		super.clear();

	// Javaport: not yet available. TODO
	//	assert (m_oContentDisplay == null);
	//	assert (m_oCaptionDisplay == null);
	}

	public void initialize(Element oNode) {
		//Strategy
		//Initialize the box model for a text based object (field/draw)
		//1)Read initial box model settings from node, including
		//-x,y,anchor
		//-margins
		//-border
		//-caption info
		//-initialize content (textual in this case)
		//2)Then format the box model into subregions (content/caption) given these settings.
		//  This will automatically account for min/max ranges
		//3)Rotate the box model, as appropriate.
		//4)Record the visual extent of the box model

		assert (oNode != null);
		assert (oNode instanceof Field || oNode instanceof Draw);
		if (! isBoxModelCompatible(oNode))
			return;
		checkProxyStatus(oNode);

		//Read ne w/h, determine growability and if applicable the ne min/max ranges
		if (! isProxy())
			initWidthHeight(oNode, true);

		//Read anchorPoint
		initAnchorPoint(oNode);

		//Read ne, content and caption margins
		initMargins(oNode);

		//Read border margins, content border margins
		initBorder(oNode);

		if (! isProxy()) {
			//Create stream, apply attributes. Do not set w/h of stream. Do not create display.
			//For caption, determine if caption present (w/wo text), reserved space if any.
			initCaption(oNode);

			//Create stream, apply attributes. Do not set w/h of stream. Do not create display.
			initContent(oNode);

			//Format internals based on the info so far
			format(oNode);

		} else {
			//Create stream, apply attributes.
			initContent(oNode);
			if (oNode instanceof Field) {
				// if this is a field, we still have to render content, so make sure the 
				// display is created.
				createDisplay(m_oContentExtent.width(), 
							  m_oContentExtent.width(), 
							  m_oContentExtent.height(), 
							  m_oContentExtent.height());
			}
		}
		//Once formatted, perform any rotation
		initRotatedContentTopLeft();

		respectRotation(oNode);

		//Finally, determine the visual extent.
		initVisualExtent(oNode);
	}

	public void reinitialize(Element oNode) {
		if ((! m_bKeepContentTextStream && ! m_bKeepCaptionTextStream)) {
			super.reinitialize(oNode);
			return;
		}
		else {
			//Don't do clean up here. We want to keep caption and content text stream alive here.
			assert (oNode != null);
			initialize(oNode);
		}
	}

	public boolean hasContent() {
		return true; //By definition, always
	}

	public String getContentByType(String sType,
								   List colorTable,
								   List fontTable) {
		assert (null != getContent());
		if (null != getContent())
			return getStringByType(getContent(), m_oAmbientTextAttr, sType, m_oStartContentPosn, m_oEndContentPosn, colorTable, fontTable);
		else
			return "";
	}

	public boolean enumerateContent(LayoutHandler pHandler,
									CoordPair oLocalOrigin,
									boolean bWrap,
									boolean bTruncate /* = false */,
									Rect oInvalidatedRect /* = Rect.ZERO */) {
		CoordPair oNewOrigin = oLocalOrigin;
		Rect oInvalid = oInvalidatedRect;

		if (oInvalid.width().equals(UnitSpan.ZERO) && oInvalid.height().equals(UnitSpan.ZERO))
			oInvalid = Rect.ZERO;

		if (m_bSplitContent) {
			oInvalid = m_oContentDisplayExtent;
			oNewOrigin = new CoordPair(oNewOrigin.x(), oNewOrigin.y().subtract(m_oContentDisplayExtent.top()));
		}

		boolean bSplitContentFits = true;

		if (m_oContentDisplayExtent.width().gt(getContentExtent().width())) {
			oInvalid = oInvalid.width(getContentExtent().width(), false);
			bSplitContentFits = false;
		}

		if (m_oContentDisplayExtent.height().gt(getContentExtent().height())) {
			oInvalid = oInvalid.height(getContentExtent().height(), false);
			bSplitContentFits = false;
		}

		boolean bFits = m_oGfxLayoutEnv.renderText(pHandler,
												   getContentDisplay(),
												   oNewOrigin,
												   bWrap,
												   bTruncate,
												   m_oRotationAngle,
												   oInvalid);

		if (m_bSplitContent)
			return bSplitContentFits;

		return bFits;
	}

	public void resizeToNominal(UnitSpan oW, UnitSpan oH, Element oNode) {
		//Strategy
		//We're resizing the box model, ie as if we grabbed the
		//corner selection handle. We are not respecting min/max settings, we are overriding them

		//Watson 1183952/1419109: nominal w/h is always considered to conform to the x,y axis. In other words
		//left-right is always the width, top-bottom direction is always height, even when objects are rotated.
		//So if the nominal width is being modified, make sure we take into account any rotation.
		if (m_bWasRotated && (m_oRotationAngle.degrees() == 90 || m_oRotationAngle.degrees() == 270)) {
			//90 or 270 degree rotation means we flip and set nominal height because when we reformat it becomes the width.
			setGrowableH(oW, oW);
			setGrowableW(oH, oH);		
		}
		else {
			setGrowableW(oW, oW);
			setGrowableH(oH, oH);
		}
		
		format(oNode);

		//Once formatted, perform any rotation
		initRotatedContentTopLeft();
		respectRotation(oNode);

		//Finally, determine the visual extent.
		initVisualExtent(oNode);
	}

	public void resizeToNominalWidth(UnitSpan oW, Element oNode) {
		//Strategy
		//What are we trying to do here? Like resizeToNominal(w,h,)
		//the box model is being resized, ie as if we've grabbed a sizing
		//handle. So should we respect min/max? What if object is not even growable?
		//For now we resize regardless of anything else.
		assert (UnitSpan.ZERO.lte(oW));

		//Watson 1183952: nominal w/h is always considered to conform to the x,y axis. In other words
		//left-right is always the width, top-bottom direction is always height, even when objects are rotated.
		//So if the nominal width is being modified, make sure we take into account any rotation.
		if (m_bWasRotated) {
			//90 or 270 degree rotation means we flip and set nominal height because when we reformat it becomes the width.
			if (m_oRotationAngle.degrees() == 90 || m_oRotationAngle.degrees() == 270) {
				setGrowableH(oW, oW);
			}
			else
				setGrowableW(oW, oW); //turns off growableness, see comments above
		}
		else
			setGrowableW(oW, oW); //turns off growableness, see comments above
		
		//reformat
		format(oNode);

		//Once formatted, perform any rotation
		initRotatedContentTopLeft();
		respectRotation(oNode);

		//Finally, determine the visual extent.
		initVisualExtent(oNode);
	}

	public void resizeToContent(UnitSpan oW, UnitSpan oH, Element oNode) {
		//Should not be called for this object
		assert (false);
	}

	public boolean splitImpl(UnitSpan oVSplit,
							BoxModelLayout pNewBM,
							Element oNode) {
		//Note : Changes to this function in terms of where the split point lands should also be reflected in ::calculateSplitPoint()
		
		//If this was rotated splitting is not possible
		if (m_bWasRotated)
			return false;

		boolean bSplit = false;

		// Does split pt cut of the box model?
		if (oVSplit.gt(UnitSpan.ZERO) && oVSplit.lt(getHeight())) {
			//Does split pt cut the content and not top/bottom margins or top/bottom caption?
			if (oVSplit.gte(m_oContentExtent.top()) && oVSplit.lte(m_oContentExtent.bottom())) {
				if (! m_bSplitContent) {
					m_oContentDisplayExtent = m_oContentDisplayExtent.width(getContentExtent().width(), false);
					m_oContentDisplayExtent = m_oContentDisplayExtent.height(getContentExtent().height(), false);

					if (m_bHasCaption) {
						m_oCaptionDisplayExtent = m_oCaptionDisplayExtent.width(getCaptionExtent().width(), false);
						m_oCaptionDisplayExtent = m_oCaptionDisplayExtent.height(getCaptionExtent().height(), false);
					}
				}

				assert (null != getContentDisplay());
				assert (null != getCaptionDisplay() || ! m_bHasCaption);

				UnitSpan oContentDisplayOffset = m_oContentDisplayExtent.top();
				assert (UnitSpan.ZERO.lte((oVSplit.subtract(m_oContentExtent.top()).add(oContentDisplayOffset))));

				TextPosn oPrevEndContentPosn = m_oEndContentPosn;
				boolean bEndsInNewLine = false;
				UnitSpan oContentSplit = UnitSpan.ZERO;
			// Javaport: not available.	TODO
			//	oContentSplit = getContentDisplay().split(oVSplit.subtract(m_oContentExtent.top()).add(oContentDisplayOffset), m_oEndContentPosn, bEndsInNewLine);
				UnitSpan oContentDisplayBottom = m_oContentDisplayExtent.bottom();

				// watson 1096277
				// If the split point to close to the bottom of the contentExtent (there 
				// should be a bottom margin otherwise we wondn't be this far in this method)  
				// we will split one line above. The reason is because we don't want to end up  
				// with a box model that doesn't have any content (or too little content e.i
				// not even big enough to see a full line).
				Rect oLineRect = Rect.ZERO;
			// Javaport: not available.	 TODO
			//	m_oEndContentPosn.caretPos(oLineRect, true);
				if (oLineRect.height().gt((oContentDisplayBottom.subtract(oContentSplit)))) {
					// try the split again... this time one line up. 
				// Javaport: not available.	 TODO
				//	oContentSplit = getContentDisplay().split(oVSplit.subtract(m_oContentExtent.top()).add(oContentDisplayOffset.subtract(oLineRect.height())), m_oEndContentPosn, bEndsInNewLine);
					assert (oContentSplit.lt(oContentDisplayBottom));
				}    

				// if we try to split to close to the beginning of the contentExtext... don't 
				// cut a boxModel when one of the piece won't have any content or very little 
				// content (i.e less than one line)
			// Javaport: not available.	 TODO
			//	m_oEndContentPosn.caretPos(oLineRect, true);
				if (oLineRect.height().gt(oContentSplit.subtract(m_oContentDisplayExtent.top()))) {
					// reset the member variables that we changed
					if (! m_bSplitContent) {
						m_oContentDisplayExtent = Rect.ZERO; 
						
						if (m_bHasCaption)
							m_oCaptionDisplayExtent = Rect.ZERO;
					}

					m_oEndContentPosn = oPrevEndContentPosn;

					return false;
				}

				Rect oOldNE = getNominalExtent();

				//Does split pt cut the content display?
				if (oContentSplit.gte(oContentDisplayOffset) && oContentSplit.lte(oContentDisplayBottom)) {
					Rect oNewContentDisplayExtent = m_oContentDisplayExtent;
					oNewContentDisplayExtent = oNewContentDisplayExtent.topBottom(oContentSplit, oContentDisplayBottom);
					m_oContentDisplayExtent = m_oContentDisplayExtent.topBottom(oContentDisplayOffset, oContentSplit);

					//Create 2nd box model
					pNewBM = new BoxModelContent(m_oGfxLayoutEnv, false);
					BoxModelContent pBMContent = (BoxModelContent) pNewBM;

					pBMContent.m_bSplitContent = true;
					pBMContent.m_oStartContentPosn = m_oEndContentPosn;
					pBMContent.m_oEndContentPosn = oPrevEndContentPosn;				

					if (bEndsInNewLine)
						m_oEndContentPosn.prevUserPosn();	// don't include trailing newline in first half

					m_bSplitContent = true;

					// If Caption is left or right	. split caption
					// If Caption is top			. no caption on 2nd box model (nothing needs to be set)
					// If Caption is bottom			. no caption on the 1st box model (caption on the 2nd box model)
					// If no Caption				. nothing needs to be done
					Rect oNewCaptionDisplayExtent = m_oCaptionDisplayExtent;
					if (m_bHasCaption) {
						assert (m_eCaptionPlacement != EnumAttr.PLACEMENT_INLINE);

						if (m_eCaptionPlacement == EnumAttr.PLACEMENT_LEFT ||
							m_eCaptionPlacement == EnumAttr.PLACEMENT_RIGHT ||
							m_eCaptionPlacement == EnumAttr.PLACEMENT_BOTTOM) {
							pBMContent.m_bHasCaption = true;
							pBMContent.m_bReserveCaptionSpace = m_bReserveCaptionSpace;
							pBMContent.m_eCaptionPresence = m_eCaptionPresence;
							pBMContent.m_eCaptionPlacement = m_eCaptionPlacement;
							pBMContent.m_oReserveCaptionSpace = m_oReserveCaptionSpace;

							if (m_eCaptionPlacement == EnumAttr.PLACEMENT_BOTTOM) {
								m_bHasCaption = false;
							}
							else {
								UnitSpan oCaptionDisplayOffset = m_oCaptionDisplayExtent.top();
								assert (UnitSpan.ZERO.lte(oVSplit.subtract(m_oCaptionExtent.top()).add(oCaptionDisplayOffset)));

								TextPosn oPrevEndCaptionPosn = new TextPosn(m_oEndCaptionPosn);
								bEndsInNewLine = false;
								UnitSpan oCaptionSplit = UnitSpan.ZERO;
							// Javaport: not available.	 TODO
							//	oCaptionSplit = getCaptionDisplay().split(oVSplit.subtract(m_oCaptionExtent.top()).add(oCaptionDisplayOffset), m_oEndCaptionPosn, bEndsInNewLine);
								UnitSpan oCaptionDisplayBottom = m_oCaptionDisplayExtent.bottom();

								oNewCaptionDisplayExtent = oNewCaptionDisplayExtent.topBottom(oCaptionSplit, oCaptionDisplayBottom);
								m_oCaptionDisplayExtent = m_oCaptionDisplayExtent.topBottom(oCaptionDisplayOffset, oCaptionSplit);

								pBMContent.m_bSplitCaption = true;
								pBMContent.m_oStartCaptionPosn = m_oEndCaptionPosn;
								pBMContent.m_oEndCaptionPosn = oPrevEndCaptionPosn;

								if (bEndsInNewLine)
									m_oEndCaptionPosn.prevUserPosn();	// don't include trailing newline in first half

								m_bSplitCaption = true;
							}
						}
					}

					pBMContent.initializeSplit(oNode, /*m_oContentDisplay, */ oNewContentDisplayExtent, /* m_oCaptionDisplay, */ oNewCaptionDisplayExtent);
					pBMContent.disableTopMargin();
					pBMContent.disableContentTopMargin();

					if (m_bSplitCaption) {
						pBMContent.disableCaptionTopMargin();
						disableCaptionBottomMargin();
					}

					//This box model was split. It is no longer growable
					//nor does it have it's original a min/max
					//Shrink wrap this box model around content
					disableBottomMargin();
					disableContentBottomMargin();

					boolean bWasHGrowable = hasGrowableH();
					setGrowableH(UnitSpan.ZERO, new UnitSpan(UnitSpan.INCHES_72K, -1));
					format(oNode);

					// get the new height
					UnitSpan oNewNEHeight = getHeight();
		
					// reset to non-growable if it wasn't to begin with
					if (! bWasHGrowable)
						setGrowableH(oNewNEHeight, oNewNEHeight);
					
					//growable or not we don't want to take more space that we were
					//allocated... so this second box model must not be bigger than the delta
					//between the original height and the new height.

					//Shrink 2nd box model piece by the difference in nominal height
					UnitSpan oNEDelta =  oOldNE.height().subtract(oNewNEHeight);
					pNewBM.resizeToNominal(oOldNE.width(), oNEDelta, oNode);

					// make sure that this new box model knows that it 
					// is part of a growable field
					if (bWasHGrowable)
						pBMContent.setGrowableH(UnitSpan.ZERO, new UnitSpan(UnitSpan.INCHES_72K, -1));

					//watson 1820449 - carry over the forced growable setting
					pBMContent.setForcedGrowableH(hasForcedGrowableH());
					pBMContent.setForcedGrowableW(hasForcedGrowableW());

					bSplit = true;
				}
			}
		}

		return bSplit;
	}

	public boolean hasSplit() {
		return m_bSplitContent || m_bSplitCaption;
	}		

	public boolean isFontSubstituted() {
		boolean bIsFontSubstituted = false;
		TextDisplay pDisp = getCaptionDisplay();
		if (pDisp != null)
			bIsFontSubstituted = pDisp.hasFontSubstitution();
		if (! bIsFontSubstituted) {
			pDisp = getContentDisplay();
			if (pDisp != null)
				bIsFontSubstituted = pDisp.hasFontSubstitution();
		}
		return bIsFontSubstituted;
	}

	public boolean hasGrowableW() {
		return	super.hasGrowableW() || m_bForcedGrowableW;
	}

	public boolean hasGrowableH() {
		return	super.hasGrowableH() || m_bForcedGrowableH;
	}

	public void	setForcedGrowableW(boolean bForcedGrowableW /* = true */) {
		m_bForcedGrowableW = bForcedGrowableW;
	}

	public void	setForcedGrowableH(boolean bForcedGrowableH /* = true */) {
		m_bForcedGrowableH = bForcedGrowableH;
	}

	public boolean	hasForcedGrowableW() {
		return	m_bForcedGrowableW;
	}

	public boolean	hasForcedGrowableH() {
		return	m_bForcedGrowableH;
	}

	/**
	 * Return the text range associated with the content of this box model.
	 * @param oStartPosn the start position within the text stream.
	 * @param oEndPosn the end position within the text stream.
	 * @return true if the box model has been split, false otherwise.
	 */
	// JavaPort: Todo: oStartPosn and oEndPosn are passed by reference in C++, but by value here.
	public boolean getContentRange(ObjectHolder oStartPosn, ObjectHolder oEndPosn) {
		oStartPosn.value = m_oStartContentPosn;
		oEndPosn.value = m_oEndContentPosn;
		return hasSplit();
	}

	/**
	 *	Retrieve the max chars attribute of the text node.
	 */
	public boolean getComb(Element oNode, Element oCurrentUI, IntegerHolder nNumCells) {
		if (oNode == null)
			return false;
		boolean	bIsComb = false;
		nNumCells.value = 0;
		Element oCombNode = oCurrentUI.peekElement(XFA.COMBTAG, false, 0);
		if (oCombNode != null) {
			bIsComb = true;
			Int oCells = (Int) oCombNode.getAttribute(XFA.NUMBEROFCELLSTAG);
			nNumCells.value = oCells.getValue();
			if (nNumCells.value < 1) {
				nNumCells.value = 1;  // minimum of 1 cell for combs
				Element oValueNode = oNode.peekElement(XFA.VALUETAG, false, 0);
				if (oValueNode != null) {
					Element oTextNode = oValueNode.peekElement(XFA.TEXTTAG, false, 0);
					if (oTextNode != null) {
						Int oMaxChars = (Int) oTextNode.getAttribute(XFA.MAXCHARSTAG);
						if (oMaxChars != null) {
							nNumCells.value = oMaxChars.getValue();
							if (nNumCells.value < 1) nNumCells.value = 1;
						}
					}
				}
			}
		}
		return bIsComb;
	}

	public boolean getKeepContentTextStreamFlag() {
		return	m_bKeepContentTextStream;
	 }

 	public void setKeepContentTextStreamFlag(boolean bKeepTextStream) {
		m_bKeepContentTextStream = bKeepTextStream;
	}

	//interface for box model text editing

	/**
	 * Text editing interface
	 * Update box model based on current stream state(s)
	 */
	public void update(Element oNode) {
		//
		//Resize to the current content
		//
		assert (! m_bWasRotated);
		//
		//If! growable, it never changes
		//
		if (! m_bGrowableW && ! m_bGrowableH
					&& ! m_bForcedGrowableW && ! m_bForcedGrowableH)
			return;
		//
		//Dont' support editing of rotated objects at this time
		//
		if (m_bWasRotated)
			return;
		//
		//format internal subregions
		//
		format(oNode);
		initVisualExtent(oNode);
	}

	public TextDisplay getContentDisplay() {
	// Javaport: not yet available. TODO
	//	if (m_oContentDisplay != null)
	//		return m_oContentDisplay.getDisplay();
		throw new ExFull(ResId.UNSUPPORTED_OPERATION, "BoxModelContent#getContentDisplay");
	}

	public TextRegion getContent() {
	// Javaport: not yet available. TODO
	//	if (m_oContentDisplay != null)
	//		return m_oContentDisplay.getRegion();
		return null;
	}

	public TextRange getContentRangeAttrs() {
	// Javaport: not yet available. TODO
	//	assert (m_oContentDisplay != null);
	//	return m_oContentDisplay.getRangeAttr();
		return null;
	}
		
	//interface used for splitting
	//returns acceptable split point <= suggested split point.

	public UnitSpan calculateSplitPoint(UnitSpan oSuggestedVSplit) {
		//Strategy
		//Given a suggested split point from top of nominal extent, calculate an acceptable
		//split point that is less than or equal to oSuggestSplit where this object can actually be split.
			
		//Does split point event cut through the box model?
		if (oSuggestedVSplit.gt(UnitSpan.ZERO) && oSuggestedVSplit.lte(getHeight())) {
			//Does split pt cut through top margin or top caption?
			if (oSuggestedVSplit.lt(m_oContentExtent.top())) {
				return UnitSpan.ZERO;
			}
			//Does split pt cut through bottom margin or bottom caption?
			if (oSuggestedVSplit.gt(m_oContentExtent.bottom())) {
				return m_oContentExtent.bottom();
			}	
			
			//Otherwise defer to the text display to identify a good split position
			if (null != getContentDisplay()) {
				//Of course need to take into account our display offset into the stream (aka any previous split piece(s))
				//UnitSpan oSuggestedTextContentSplitPt = m_oContentDisplayExtent.top().add(oSuggestedVSplit).subtract(m_oContentExtent.top());
				UnitSpan oTextContentSplitPt = UnitSpan.ZERO;
			// Javaport: not available.	TODO
			//	oTextContentSplitPt = getContentDisplay().split(oSuggestedTextContentSplitPt);
				
				// watson 1096277 (and thus also fixes 1222364)
				// If the split point to close to the bottom of the contentExtent (there 
				// should be a bottom margin otherwise we wondn't be this far in this method)  
				// we will split one line above. The reason is because we don't want to end up  
				// with a box model that doesn't have any content (or too little content e.i
				// not even big enough to see a full line).
				if (! m_bSplitContent) {
					m_oContentDisplayExtent = m_oContentDisplayExtent.width(getContentExtent().width(), false);
					m_oContentDisplayExtent = m_oContentDisplayExtent.height(getContentExtent().height(), false);
				}
				Rect oLineRect = Rect.ZERO;
			// Javaport: not available.	 TODO
			//	m_oEndContentPosn.caretPos(oLineRect, true);
				if (oLineRect.height().gt((m_oContentDisplayExtent.bottom().subtract(oTextContentSplitPt)))) {
					// try the split again... this time one line up. 
					throw new ExFull(ResId.UNSUPPORTED_OPERATION, "BoxModelContent#calculateSplitPoint");
				// Javaport: not available.	 TODO
				//	oTextContentSplitPt = getContentDisplay().split(oTextContentSplitPt.subtract(oLineRect.height()));				
				}    
				//If it's in the first line, then give up. There is no split point.
			// Javaport: not available.	 TODO
			//	m_oStartContentPosn.caretPos(oLineRect, true);
				if (oLineRect.bottom().gt(m_oContentDisplayExtent.top().add(oTextContentSplitPt)))
					return UnitSpan.ZERO;
				if (UnitSpan.ZERO.gt(oTextContentSplitPt))
					return UnitSpan.ZERO;

				return oTextContentSplitPt.add(m_oContentExtent.top()).subtract(m_oContentDisplayExtent.top());	
			}	
		}
		return UnitSpan.ZERO;		
	}

//internal formatting functions

	// JavaPort: TODO. oNode is never referenced?
	@FindBugsSuppress(pattern="DLS_DEAD_LOCAL_STORE")	// oConStreamMinH - false positive
	protected void format(Element oNode) {
		//Format the box model into subtract regions based on current settings.
		//Here we go (try to keep up)
		UnitSpan oCapSpaceW = UnitSpan.ZERO;
		UnitSpan oCapSpaceH = UnitSpan.ZERO;

		UnitSpan oCapStreamMinH = UnitSpan.ZERO;
		UnitSpan oCapStreamMaxH = UnitSpan.ZERO;
		UnitSpan oCapStreamMinW = UnitSpan.ZERO;
		UnitSpan oCapStreamMaxW = UnitSpan.ZERO;
		UnitSpan oConStreamMinH = UnitSpan.ZERO;
		UnitSpan oConStreamMaxH = UnitSpan.ZERO;
		UnitSpan oConStreamMinW = UnitSpan.ZERO;
		UnitSpan oConStreamMaxW = UnitSpan.ZERO;

		//sizes of box model subtract regions. Not necessarily the same as stream sizes
		UnitSpan oCaptionExtentW = UnitSpan.ZERO;
		UnitSpan oCaptionExtentH = UnitSpan.ZERO;
		UnitSpan oContentExtentW = UnitSpan.ZERO;
		UnitSpan oContentExtentH = UnitSpan.ZERO;

		//Margins
		Margins oCapMargins = getCaptionExtentMargins();
		Margins oConMargins = getContentExtentMargins();
		Margins oNEMargins = getNominalExtentMargins();

		UnitSpan oCapLeftRightMargins = oCapMargins.marginLeft().add(oCapMargins.marginRight());
		UnitSpan oCapTopBottomMargins = oCapMargins.marginTop().add(oCapMargins.marginBottom());

		UnitSpan oConLeftRightMargins = oConMargins.marginLeft().add(oConMargins.marginRight());
		UnitSpan oConTopBottomMargins = oConMargins.marginTop().add(oConMargins.marginBottom());

		UnitSpan oNELeftRightMargins = oNEMargins.marginLeft().add(oNEMargins.marginRight());
		UnitSpan oNETopBottomMargins = oNEMargins.marginTop().add(oNEMargins.marginBottom());

		if (m_bHasCaption) { //should be true if caption space of any kind, even if no text
			assert (EnumAttr.PLACEMENT_INLINE != m_eCaptionPlacement);

			if (EnumAttr.PLACEMENT_LEFT == m_eCaptionPlacement ||
			   EnumAttr.PLACEMENT_RIGHT == m_eCaptionPlacement) {
				oCapStreamMinH = m_oMinH.subtract(oNETopBottomMargins).subtract(oCapTopBottomMargins);
				if (m_bGrowableH && m_bInfiniteMaxH)
					oCapStreamMaxH = new UnitSpan(UnitSpan.INCHES_72K, -1);
				else
					oCapStreamMaxH = m_oMaxH.subtract(oNETopBottomMargins).subtract(oCapTopBottomMargins);

				if (m_bReserveCaptionSpace) {
					oCapStreamMinW = m_oReserveCaptionSpace.subtract(oCapLeftRightMargins);
					oCapStreamMaxW = oCapStreamMinW;
				}
				else {
					oCapStreamMinW = m_oMinW.subtract(oNELeftRightMargins).subtract(oCapLeftRightMargins);
					if (m_bGrowableW && m_bInfiniteMaxW)
						oCapStreamMaxW = new UnitSpan(UnitSpan.INCHES_72K, -1);
					else
						oCapStreamMaxW = m_oMaxW.subtract(oNELeftRightMargins).subtract(oCapLeftRightMargins);
				}
			}
			else if (EnumAttr.PLACEMENT_TOP == m_eCaptionPlacement ||
					EnumAttr.PLACEMENT_BOTTOM == m_eCaptionPlacement) {
				oCapStreamMinW = m_oMinW.subtract(oNELeftRightMargins).subtract(oCapLeftRightMargins);
				if (m_bGrowableW && m_bInfiniteMaxW)
					oCapStreamMaxW = new UnitSpan(UnitSpan.INCHES_72K, -1);
				else
					oCapStreamMaxW = m_oMaxW.subtract(oNELeftRightMargins).subtract(oCapLeftRightMargins);
				if (m_bReserveCaptionSpace) {
					oCapStreamMinH = m_oReserveCaptionSpace.subtract(oCapTopBottomMargins);
					oCapStreamMaxH = oCapStreamMinH;
				}
				else {
					oCapStreamMinH = m_oMinH.subtract(oNETopBottomMargins.subtract(oCapTopBottomMargins));
					if (m_bGrowableH && m_bInfiniteMaxH)
						oCapStreamMaxH = new UnitSpan(UnitSpan.INCHES_72K, -1);
					else
						oCapStreamMaxH = m_oMaxH.subtract(oNETopBottomMargins).subtract(oCapTopBottomMargins);
				}
			}

			if (oCapStreamMinW.lt(UnitSpan.ZERO))
				oCapStreamMinW = UnitSpan.ZERO;
			if (oCapStreamMinH.lt(UnitSpan.ZERO))
				oCapStreamMinH = UnitSpan.ZERO;

			//Set caption stream sizes, then create display. This is more efficient
			//because the display would do formatting more than once.
			if (! m_bSplitCaption) {
			// Javaport: TODO
			//	getCaption().setMinWidth(oCapStreamMinW);
			//	getCaption().setMaxWidth(oCapStreamMaxW);
			//	getCaption().setMinHeight(oCapStreamMinH);
			//	getCaption().setMaxHeight(oCapStreamMaxH);
			}
		//	if (null == getCaptionDisplay())
		//		m_oCaptionDisplay.createDisplay(null, m_oGfxLayoutEnv);

			//
			//Unless a reserved value is specified for caption, it will automaticaly take
			//all the room it needs
			//Calculate caption extent w/h.
			//
			if (EnumAttr.PLACEMENT_LEFT == m_eCaptionPlacement ||
			   EnumAttr.PLACEMENT_RIGHT == m_eCaptionPlacement) {
			// Javaport: TODO
			//	Rect rcRuntimeExtent;
			//	if (m_bSplitCaption)
			//		rcRuntimeExtent = m_oCaptionDisplayExtent;
			//	else
			//		rcRuntimeExtent = getCaptionDisplay().runtimeExtent();
			//
			//	oCaptionExtentW = rcRuntimeExtent.width();
			//	oCaptionExtentH = rcRuntimeExtent.height();
			//
				if (m_bReserveCaptionSpace) {
					assert (oCapStreamMinW.equals(oCapStreamMaxW));
					oCaptionExtentW = oCapStreamMaxW;
				}
				//Clamp captions heights to any fixed height
				if (! m_bGrowableH) {
					oCaptionExtentH = oCapStreamMaxH;
				}
			
				oCapSpaceW = oCaptionExtentW.add(oCapLeftRightMargins);
				oCapSpaceH = UnitSpan.ZERO;
			}
			else if (EnumAttr.PLACEMENT_TOP == m_eCaptionPlacement ||
					EnumAttr.PLACEMENT_BOTTOM == m_eCaptionPlacement) {
			// Javaport: TODO
			//	Rect rcRuntimeExtent;
			//	if (m_bSplitCaption)
			//		rcRuntimeExtent = m_oCaptionDisplayExtent;
			//	else
			//		rcRuntimeExtent = getCaptionDisplay().runtimeExtent();

			//	oCaptionExtentW = rcRuntimeExtent.width();
			//	oCaptionExtentH = rcRuntimeExtent.height();

				if (m_bReserveCaptionSpace) {
					assert (oCapStreamMinH.equals(oCapStreamMaxH));
					oCaptionExtentH = oCapStreamMaxH;
				}
				else {
					// Javaport: TODO.  For now, just throw if sized-to-fit.
					throw new ExFull(ResId.UNSUPPORTED_OPERATION, "BoxModelContent#format");
				}
				//Clamp captions width to any fixed width
				if (! m_bGrowableW) {
					oCaptionExtentW = oCapStreamMaxW;
				}
				oCapSpaceH = oCaptionExtentH.add(oCapTopBottomMargins);
				oCapSpaceW = UnitSpan.ZERO;
			}
		}//endif caption

		//Figure out what range of room is left for content
		oConStreamMinW = m_oMinW.subtract(oNELeftRightMargins).subtract(oConLeftRightMargins).subtract(oCapSpaceW);
		oConStreamMaxW = m_oMaxW.subtract(oNELeftRightMargins).subtract(oConLeftRightMargins).subtract(oCapSpaceW);

		oConStreamMinH = m_oMinH.subtract(oNETopBottomMargins).subtract(oConTopBottomMargins).subtract(oCapSpaceH);
		oConStreamMaxH = m_oMaxH.subtract(oNETopBottomMargins).subtract(oConTopBottomMargins).subtract(oCapSpaceH);

		//Protect against bad ranges
		if (oConStreamMinW.lt(UnitSpan.ZERO))
			oConStreamMinW = UnitSpan.ZERO;
		if (oConStreamMinH.lt(UnitSpan.ZERO))
			oConStreamMinH = UnitSpan.ZERO;
		if ((m_bGrowableW && m_bInfiniteMaxW) || m_bForcedGrowableW)
			oConStreamMaxW = new UnitSpan(UnitSpan.INCHES_72K, -1);
		else {
			if (oConStreamMaxW.lt(UnitSpan.ZERO))
				oConStreamMaxW = UnitSpan.ZERO;
		}
		if ((m_bGrowableH && m_bInfiniteMaxH) || m_bForcedGrowableH)
			oConStreamMaxH = new UnitSpan(UnitSpan.INCHES_72K, -1);
		else {
			if (oConStreamMaxH.lt(UnitSpan.ZERO))
				oConStreamMaxH = UnitSpan.ZERO;
		}

		createDisplay(oConStreamMinW, oConStreamMaxW, oConStreamMinH, oConStreamMaxH);

		//Now determine content extent. This is based on whether growable or not, ie difference
		//between runtime width vs fixed width
		if (m_bGrowableW || m_bForcedGrowableW) {
			Rect oRuntimeExtent;
			if (m_bSplitContent)
				oRuntimeExtent = m_oContentDisplayExtent;
			else
				oRuntimeExtent = getContentDisplay().runtimeExtent();

			oContentExtentW = oRuntimeExtent.right();
			if (oContentExtentW.lt(oConStreamMinW))
				oContentExtentW = oConStreamMinW;
			else if (! m_bForcedGrowableW && oContentExtentW.gt(oConStreamMaxW) && ! m_bInfiniteMaxW)		// If we force to grow, don't set theraint
				oContentExtentW = oConStreamMaxW;
			assert (! oContentExtentW.gt(oConStreamMaxW) || m_bInfiniteMaxW || m_bForcedGrowableW);
		}
		else {
			assert (oConStreamMinW.equals(oConStreamMaxW));
			oContentExtentW = oConStreamMaxW;
		}
		if (m_bGrowableH || m_bForcedGrowableH) {
			Rect oRuntimeExtent;
			if (m_bSplitContent)
				oRuntimeExtent = m_oContentDisplayExtent;
			else
				oRuntimeExtent = getContentDisplay().runtimeExtent();

			oContentExtentH = oRuntimeExtent.height();
			if (oContentExtentH.lt(oConStreamMinH))
				oContentExtentH = oConStreamMinH;
			else if (! m_bForcedGrowableH && oContentExtentH.gt(oConStreamMaxH) && ! m_bInfiniteMaxH)	// If we force to grow, don't set theraint
				oContentExtentH = oConStreamMaxH;

			assert (! oContentExtentH.gt(oConStreamMaxH) || m_bInfiniteMaxH || m_bForcedGrowableH);
		}
		else {
			assert (oConStreamMinH.equals(oConStreamMaxH));
			oContentExtentH = oConStreamMaxH;
		}

		//All that's left is to set m_oNE, m_oContentExtent and m_oCaptionExtent
		if (m_bHasCaption) {
			if (EnumAttr.PLACEMENT_LEFT == m_eCaptionPlacement) {
				//The determinant height is content+con margins.
				m_oNE = m_oNE.width(oNELeftRightMargins.add(oCapLeftRightMargins).add(oCaptionExtentW).add(oConLeftRightMargins).add(oContentExtentW), false);
				if (oContentExtentH.add(oConTopBottomMargins).gt(oCaptionExtentH.add(oCapTopBottomMargins))) {
					m_oNE = m_oNE.height(oNETopBottomMargins.add(oContentExtentH).add(oConTopBottomMargins), false);
					oCaptionExtentH = oContentExtentH.add(oConTopBottomMargins).subtract(oCapTopBottomMargins);
				}
				else {
					//If this is a split piece of content and the content is smaller, it takes precedence.
					//So shrink the caption to match. Otherwise we get case where field
					//grows to accomodate caption, which is likely empty space anyway.
					//If not split then the caption does indeed take precedence
					if (m_bSplitCaption) {
						m_oNE = m_oNE.height(oNETopBottomMargins.add(oContentExtentH).add(oConTopBottomMargins), false);
						oCaptionExtentH = m_oNE.height().subtract(oCapTopBottomMargins).add(oNETopBottomMargins);
						m_oCaptionDisplayExtent = m_oCaptionDisplayExtent.height(oCaptionExtentH, false);
					}
					else {
						m_oNE = m_oNE.height(oNETopBottomMargins.add(oCaptionExtentH).add(oCapTopBottomMargins), false);
						oContentExtentH = oCaptionExtentH.add(oCapTopBottomMargins).subtract(oConTopBottomMargins);
					}
				}

				m_oCaptionExtent = m_oCaptionExtent.leftWidth(oNEMargins.marginLeft().add(oCapMargins.marginLeft()),
										   oCaptionExtentW);
				m_oCaptionExtent = m_oCaptionExtent.topHeight(oNEMargins.marginTop().add(oCapMargins.marginTop()),
										   oCaptionExtentH);

				m_oContentExtent = m_oContentExtent.leftWidth(oNEMargins.marginLeft().add(oCapLeftRightMargins).add(oCaptionExtentW).add(oConMargins.marginLeft()),
											oContentExtentW);
				m_oContentExtent = m_oContentExtent.topHeight(oNEMargins.marginTop().add(oConMargins.marginTop()),
											oContentExtentH);
			}
			else if (EnumAttr.PLACEMENT_RIGHT == m_eCaptionPlacement) {
				//The determinant height is content+con margins.
				m_oNE = m_oNE.width(oNELeftRightMargins.add(oCapLeftRightMargins).add(oCaptionExtentW).add(oConLeftRightMargins).add(oContentExtentW), false);
				if (oContentExtentH.add(oConTopBottomMargins).gt(oCaptionExtentH.add(oCapTopBottomMargins))) {
					m_oNE = m_oNE.height(oNETopBottomMargins.add(oContentExtentH).add(oConTopBottomMargins), false);
					oCaptionExtentH = oContentExtentH.add(oConTopBottomMargins).subtract(oCapTopBottomMargins);
				}
				else {
					//If this is a split piece of content and the content is smaller, it takes precedence.
					//So shrink the caption to match. Otherwise we get case where field
					//grows to accomodate caption, which is likely empty space anyway.
					//If not split then the caption does indeed take precedence
					if (m_bSplitCaption) {
						m_oNE = m_oNE.height(oNETopBottomMargins.add(oContentExtentH).add(oConTopBottomMargins), false);
						oCaptionExtentH = m_oNE.height().subtract(oCapTopBottomMargins.add(oNETopBottomMargins));
						m_oCaptionDisplayExtent = m_oCaptionDisplayExtent.height(oCaptionExtentH, false);
					}
					else {
						m_oNE = m_oNE.height(oNETopBottomMargins.add(oCaptionExtentH).add(oCapTopBottomMargins), false);
						oContentExtentH = oCaptionExtentH.add(oCapTopBottomMargins).subtract(oConTopBottomMargins);
					}
				}

				m_oCaptionExtent = m_oCaptionExtent.leftWidth(oNEMargins.marginLeft().add(oConLeftRightMargins).add(oContentExtentW).add(oCapMargins.marginLeft()),
										   oCaptionExtentW);
				m_oCaptionExtent = m_oCaptionExtent.topHeight(oNEMargins.marginTop().add(oCapMargins.marginTop()),
										   oCaptionExtentH);

				m_oContentExtent = m_oContentExtent.leftWidth(oNEMargins.marginLeft().add(oConMargins.marginLeft()),
										 oContentExtentW);
				m_oContentExtent = m_oContentExtent.topHeight(oNEMargins.marginTop().add(oConMargins.marginTop()),
											oContentExtentH);
			}
			else if (EnumAttr.PLACEMENT_TOP == m_eCaptionPlacement) {
				//What's the determinant width? content+con margins or caption+cap margins?
				//Expand the smaller to fit.
				m_oNE = m_oNE.height(oNETopBottomMargins.add(oCapTopBottomMargins).add(oCaptionExtentH).add(oConTopBottomMargins).add(oContentExtentH), false);
				if (oContentExtentW.add(oConLeftRightMargins).gt(oCaptionExtentW.add(oCapLeftRightMargins))) {
					m_oNE = m_oNE.width(oNELeftRightMargins.add(oContentExtentW).add(oConLeftRightMargins), false);
					oCaptionExtentW = oContentExtentW.add(oConLeftRightMargins).subtract(oCapLeftRightMargins);
				}
				else {
					m_oNE = m_oNE.width(oNELeftRightMargins.add(oCaptionExtentW).add(oCapLeftRightMargins), false);
					oContentExtentW = oCaptionExtentW.add(oCapLeftRightMargins).subtract(oConLeftRightMargins);
				}

				m_oCaptionExtent = m_oCaptionExtent.leftWidth(oNEMargins.marginLeft().add(oCapMargins.marginLeft()),
										   oCaptionExtentW);
				m_oCaptionExtent = m_oCaptionExtent.topHeight(oNEMargins.marginTop().add(oCapMargins.marginTop()),
										   oCaptionExtentH);

				m_oContentExtent = m_oContentExtent.leftWidth(oNEMargins.marginLeft().add(oConMargins.marginLeft()),
											oContentExtentW);
				m_oContentExtent = m_oContentExtent.topHeight(oNEMargins.marginTop().add(oCapTopBottomMargins).add(oCaptionExtentH).add(oConMargins.marginTop()),
											oContentExtentH);
			}
			else if (EnumAttr.PLACEMENT_BOTTOM == m_eCaptionPlacement) {
				//What's the determinant width? content+con margins or caption+cap margins?
				//Expand the smaller to fit.
				m_oNE = m_oNE.height(oNETopBottomMargins.add(oCapTopBottomMargins).add(oCaptionExtentH).add(oConTopBottomMargins).add(oContentExtentH), false);
				if (oContentExtentW.add(oConLeftRightMargins).gt(oCaptionExtentW.add(oCapLeftRightMargins))) {
					m_oNE = m_oNE.width(oNELeftRightMargins.add(oContentExtentW).add(oConLeftRightMargins), false);
					oCaptionExtentW = oContentExtentW.add(oConLeftRightMargins).subtract(oCapLeftRightMargins);
				}
				else {
					m_oNE = m_oNE.width(oNELeftRightMargins.add(oCaptionExtentW).add(oCapLeftRightMargins), false);
					oContentExtentW = oCaptionExtentW.add(oCapLeftRightMargins).subtract(oConLeftRightMargins);
				}

				m_oCaptionExtent = m_oCaptionExtent.leftWidth(oNEMargins.marginLeft().add(oCapMargins.marginLeft()),
										   oCaptionExtentW);
				m_oCaptionExtent = m_oCaptionExtent.topHeight(oNEMargins.marginTop().add(oConTopBottomMargins).add(oContentExtentH).add(oCapMargins.marginTop()),
										   oCaptionExtentH);

				m_oContentExtent = m_oContentExtent.leftWidth(oNEMargins.marginLeft().add(oConMargins.marginLeft()),
											oContentExtentW);
				m_oContentExtent = m_oContentExtent.topHeight(oNEMargins.marginTop().add(oConMargins.marginTop()),
											oContentExtentH);
			}
			else
				assert (false); //inline, m_bHasCaption should be false


			if (! m_bSplitCaption) {
			// Javaport: TODO
			//	getCaption().setJustifyExtents(m_oCaptionExtent.width(), m_oCaptionExtent.height());
			}

			if (! m_bSplitContent) {
			// Javaport: TODO
			//	getContent().setJustifyExtents(m_oContentExtent.width(), m_oContentExtent.height());
			}
		}
		else {
			m_oCaptionExtent = Rect.ZERO;
			m_oContentExtent = m_oContentExtent.leftWidth(oNEMargins.marginLeft().add(oConMargins.marginLeft()), //left
									 oContentExtentW);//width
			m_oContentExtent = m_oContentExtent.topHeight(oNEMargins.marginTop().add(oConMargins.marginTop()), //top
									 oContentExtentH);

			if (! m_bSplitContent) {
			// Javaport: TODO
			//	getContent().setJustifyExtents(m_oContentExtent.width(), m_oContentExtent.height());
			}

			m_oNE = m_oNE.height(oNETopBottomMargins.add(oConTopBottomMargins).add(oContentExtentH), false);
			m_oNE = m_oNE.width(oNELeftRightMargins.add(oContentExtentW).add(oConLeftRightMargins), false);
		}

		//Yes, we've already calculated the nominal extent w/h.
		//But we did so by adding individual subtract region widths/height and their will inevitably be
		//round off errors. For non-growable objects at least, we'll avoid these errors by just
		//setting outright nominal extent values
		if (! m_bGrowableW && ! m_bForcedGrowableW) {
			assert (m_oMaxW.equals(m_oMinW));
			m_oNE = m_oNE.width(m_oMaxW, false);
		}
		if (! m_bGrowableH && ! m_bForcedGrowableH) {
			assert (m_oMaxH.equals(m_oMinH));
			m_oNE = m_oNE.height(m_oMaxH, false);
		}
	}

	protected void initContent(Element oNode) {
		//Strategy
		//Create the content stream, set text and other font
		//attributes. Do not create a text display at this time - it
		//will be done during formatting.
		assert (oNode instanceof Draw || oNode instanceof Field);

		// If we're a draw element and rendered from text runs, then we
		// will get our content from the stored text run definitions.
		if (isProxy() && oNode instanceof Draw)
			return;
		Element oValue = oNode.peekElement(XFA.VALUETAG, true, 0);
		Content oContent = (Content) oValue.getOneOfChild(true, true);
		UI oUI = (UI) oNode.peekElement(XFA.UITAG, true, 0);
		Element oCurrentUI = oUI.getUIElement(false);

		boolean bRichContent = false;
		//boolean bIsExData = false;
		boolean bIsField = (oNode instanceof Field);
		//boolean bMultiLine = ! bIsField;	// We'll assume not multiline for all fields, unless otherwise stated.
		//String sTextContent = null;
		//boolean bIsComb = false;
		//int nNumCells = 0;
		boolean bPasswordEdit = false;
		char uPasswordChar = 0;

		// Get the locale setting for the field/draw if specified.
		String sLang = oNode.getInstalledLocale();
		boolean bIsLocaleSpecified = !StringUtils.isEmpty(sLang);

		int oCurrentUITag = oCurrentUI.getClassTag();
//		if (oCurrentUITag == XFA.TEXTEDITTAG && bIsField) {
//			bMultiLine = Bool.getValue((EnumValue) oCurrentUI.getAttribute(XFA.MULTILINETAG));
//
//			// See if we're dealing with a comb field
//			if (! bMultiLine)
//				bIsComb = getComb(oNode, oCurrentUI, nNumCells);
//		}

//		if ((oCurrentUITag == XFA.DATETIMEEDITTAG || oCurrentUITag == XFA.NUMERICEDITTAG) && bIsField) {
//			// See if we're dealing with a comb field
//			bIsComb = getComb(oNode, oCurrentUI, nNumCells);
//		}

//		if (oCurrentUITag == XFA.CHOICELISTTAG && bIsField)
//			bMultiLine = false;

		if (oCurrentUITag == XFA.PASSWORDEDITTAG && bIsField) {
			bPasswordEdit = true;
			String oPasswordChar = oCurrentUI.peekAttribute(XFA.PASSWORDCHARTAG).toString();
			if (oPasswordChar != null) {
				String sPasswordChar = oPasswordChar;
				if (!StringUtils.isEmpty(sPasswordChar)) {
					uPasswordChar = sPasswordChar.charAt(0);
				}
			}
			if (uPasswordChar == 0x0000)
				uPasswordChar = 0x002A; // '*'
		}

		if (oContent instanceof ExDataValue) {
			//bIsExData = true;
			//Is this rich text?
			String sContentType = oContent.getAttribute(XFA.CONTENTTYPETAG).toString();
			bRichContent = (STRS.TEXTHTML.equals(sContentType));
		}

		// If it's not an field we get the text in a different way.
		// Why?  Because fields can contain picture clauses.
//		if (! bIsField) {
//			if (oContent instanceof TextValue) {
//				sTextContent = ((TextValue) oContent).getValue();
//			}
//			else if (oContent instanceof DecimalValue) {
//				double dTemp = ((DecimalValue) oContent).getValue();
//				sTextContent = Double.toString(dTemp);
//			}
//			else if (oContent instanceof IntegerValue) {
//				int nTemp = ((IntegerValue) oContent).getValue();
//				sTextContent = Integer.toString(nTemp);
//			}
//			else if (oContent instanceof FloatValue) {
//				double dTemp = ((FloatValue) oContent).getValue();
//				sTextContent = Double.toString(dTemp);
//			}
//			else if (oContent instanceof DateValue) {
//				sTextContent = ((DateValue) oContent).getValue();
//			}
//			else if (oContent instanceof DateTimeValue) {
//				sTextContent = ((DateTimeValue) oContent).getValue();
//			}
//			else if (oContent instanceof TimeValue) {
//				sTextContent = ((TimeValue) oContent).getValue();
//			}
//			else if (oContent instanceof ExDataValue) {
//		        AppModel appModel = oContent.getAppModel();
//		        
//				// watson bug 1795969  old forms need to use the legacy XHTML processing
//				boolean bLegacy = appModel.getLegacySetting(AppModel.XFA_LEGACY_V27_XHTMLVERSIONPROCESSING);
//
//				// If we are here then the content type was not
//				// text/html, but text/plain, so we will get the
//				// value here.	If it's RichText, it will be handled
//				// later in this method.
//				if (bIsExData && ! bRichContent) {
//					sTextContent = ((ExDataValue) oContent).getValue(false, false, bLegacy);
//				}
//			}
//		}
//		else {
//			if (bIsExData) {
//				AppModel appModel = oContent.getAppModel();
//				
//				// watson bug 1795969  old forms need to use the legacy XHTML processing
//				boolean bLegacy = appModel.getLegacySetting(AppModel.XFA_LEGACY_V27_XHTMLVERSIONPROCESSING);
//				
//				sTextContent = ((ExDataValue) oContent).getValue(false, false, bLegacy);
//			}
//			else {
//				// Get the picture formatted text
//				sTextContent = ((Field) oNode).getFormattedValue();
//			}
//		}

		// Init text engine members:
		// Assign a default width and height to textblock.
		// This may change once any growable-ness calculates the
		// exact content area the text is to occupy.
		if (! m_bKeepContentTextStream) {	// Do full initialization
		// Javaport: not yet available. TODO
		//	m_oContentDisplay = AXTEDisplay(new TextRegion(), m_oGfxLayoutEnv);
		//	getContent().setText(sTextContent);
		//	getContent().allowOverflow(! bMultiLine);
		//
		//	// For a comb field let the text region know how many cells there are
		//	if (bIsComb)
		//		getContent().combCells(nNumCells);
		//	getContentRangeAttrs().associate(getContent());
		//
		//	//For performance reasons, collect text attributes so they can be set all at once.
		//	//Record them in a member variable so that we can return the correct xhtml string value if asked
		//	m_oAmbientTextAttr.default();
			m_oAmbientTextAttr.transparent(true);
			m_oAmbientTextAttr.fontService(m_oGfxLayoutEnv.fontService());

			//Initialize alignment. Be aware that justifyAll is not compatible with horiz-growableness.
			//One indicates 'grow to the content extent' and the other says 'extend the content along the
			//entire extent'.
			Element oPara = oNode.peekElement(XFA.PARATAG, true, 0);
			int eVAlign = oPara.getEnum(XFA.VALIGNTAG);
			setVertAlign(eVAlign, m_oAmbientTextAttr);

			int eHAlign = oPara.getEnum(XFA.HALIGNTAG);
			if (EnumAttr.HALIGN_JUSTIFY_ALL == eHAlign && hasGrowableW())
				eHAlign = EnumAttr.HALIGN_JUSTIFY;
			setHorizAlign(eHAlign, m_oAmbientTextAttr);

			if (EnumAttr.HALIGN_RADIX == eHAlign) {
				// Check for Radix Offset
				UnitSpan oRO = new Measurement(oPara.getAttribute(XFA.RADIXOFFSETTAG)).getUnitSpan();
				m_oAmbientTextAttr.radixOffset(new TextMeasurement(oRO));
			}

			// Set the locale on the text attr if one was specified.
			if (bIsLocaleSpecified) {
				m_oAmbientTextAttr.locale(sLang);
			}

			// Initialize the font.
			setFont(oNode.peekElement(XFA.FONTTAG, true, 0), oPara, true, m_oAmbientTextAttr, sLang);

			if (bIsField) {
				Element oFormat = oNode.peekElement(XFA.FORMATTAG, false, 0);

				Picture oPicture = null;
				if (oFormat != null)
					oPicture = (Picture) oFormat.peekElement(XFA.PICTURETAG, false, 0);

				String sPicture = "";
				if (oPicture != null)
					sPicture = oPicture.getValue();

				if (!StringUtils.isEmpty(sPicture)) {
					StringBuilder sCat = new StringBuilder();
					StringBuilder sLoc = new StringBuilder();
					StringBuilder sSub = new StringBuilder();
					if (PictureFmt.isSubPicture(sPicture, 0, sCat, sLoc, sSub)
					&& sCat.toString().equals("num")
					|| PictureFmt.isNumericPicture(sPicture)) {
						if (StringUtils.isEmpty(sSub))
							sSub.append(sPicture);
						if (StringUtils.isEmpty(sLoc))
							sLoc.append(sLang);
						LcLocale oLocale = new LcLocale(sLoc.toString());
						m_oAmbientTextAttr.locale(oLocale.getIsoName());
						String sRawTextContent = ((Field) oNode).getRawValue();
						LcNum oNum = new LcNum(sRawTextContent, sLoc.toString());
						IntegerHolder oRadixPos = new IntegerHolder();
						String sRes = oNum.format(sSub.toString(), oRadixPos);
						if (!StringUtils.isEmpty(sRes))
							m_oAmbientTextAttr.radixPos(oRadixPos.value);
					}
				}

				//initialize the tab default and tab stops
				setTabs (oPara.getAttribute(XFA.TABSDEFAULTTAG),
						oPara.getAttribute(XFA.TABSTOPSTAG),
						m_oAmbientTextAttr);
			}

			if (bPasswordEdit) {
				m_oAmbientTextAttr.invisible(true);
				m_oAmbientTextAttr.invisChar(uPasswordChar);
			}

			//Apply the text attributes
		// Javaport: not yet available. TODO
		//	getContentRangeAttrs().attribute(m_oAmbientTextAttr);

			if (bRichContent) {
				AppModel appModel = oContent.getAppModel();
				
				// watson bug 1795969  old forms need to use the legacy XHTML processing
				boolean bLegacy = appModel.getLegacySetting(AppModel.XFA_LEGACY_V27_XHTMLVERSIONPROCESSING);

				String sRichText = ((ExDataValue) oContent).getValue(true, false, bLegacy);

				// Supply an ambient attribute so that we can process the
				// xhtml fragment better.  This fixes an immediate problem
				// where we need to tell text services what the ambient font is.
				// Also, pass a text resolver for any embedded field references within
				// the xhtml markup
				if (!StringUtils.isEmpty(sRichText)) {
                // Javaport: TODO
				//	assert (null != m_oGfxLayoutEnv.getTextResolver());
					if (null != m_oGfxLayoutEnv.getTextResolver()) {
						// set the context for the SOMExpression so that
						// we may be able to resolve un-qualified names
					// Javaport: not yet available. TODO
					//	LayoutTextResolver pResolver = (LayoutTextResolver) m_oGfxLayoutEnv.getTextResolver();
					//	assert (null != pResolver);
					//	pResolver.setContext(oNode);
					//
					//	TextAttr pAmbientAttr = m_oAmbientTextAttr; 
					//	pResolver.hasEmbeddedContent(false);
					//
					//	MarkupXHTMLIn oMarkup = new MarkupXHTMLIn(sRichText, null, pAmbientAttr, pResolver);
					//	getContentRangeAttrs().markup(oMarkup);
					//
					//	m_bEmbeddedContent = pResolver.hasEmbeddedContent();
					//	pResolver.hasEmbeddedContent(false);
					}
				}
			}

			// If caption is inline then be sure to insert it at start
			// of content stream. This must be done *after* resolving
			// any rich markup font changes in the content stream.
			if (hasInlineCaption() && null != getCaption()) {
				TextPosnBase oPosn = new TextPosnBase(getContent());
				oPosn.first();
			// Javaport: TODO
			//	getContent().posnInsert(oPosn, getCaption());
			}

			TextPosnBase oPosn = new TextPosnBase(getContent());

			m_oStartContentPosn = new TextPosn(oPosn);
			m_oEndContentPosn = new TextPosn(oPosn);

			m_oStartContentPosn.first();
			m_oEndContentPosn.last();

		}
	}

	public boolean hasEmbeddedContent() {
		return m_bEmbeddedContent;
	}

	protected void createDisplay(UnitSpan minW, 
									UnitSpan maxW, 
									UnitSpan minH, 
									UnitSpan maxH) {
		//Set the content ranges. Because these values were calculated with the nominal extent
		//min/max in mind the we know that the resulting content region will not result in a box
		//model that violates it's nominal range.
		//Create the content display after setting ranges - it's more efficient because
		//then the line formatting is only performed once.
		if (! m_bSplitContent) {
		// Javaport: TODO
		//	getContent().setMinWidth(minW);
		//	getContent().setMaxWidth(maxW);
		//	getContent().setMinHeight(minH);
		//	getContent().setMaxHeight(maxH);
		}
	//	if (null == getContentDisplay())
	//		m_oContentDisplay.createDisplay(null, m_oGfxLayoutEnv);
	}

	//Private initialization used during splitting

	private void initializeSplit(Element oNode,
										//	 AXTEDisplay oContent,
											 Rect oContentRect,
										//	 AXTEDisplay oCaption,
											 Rect oCaptionRect) {
	//Strategy
	//Initialize the box model for a text based object (field/draw) given
	//the initial stream of content and caption but still using box model
	//settings of xfa node.
	//1)Read initial box model settings from node, including
	//-x,y,anchor
	//-margins
	//-border
	//-initialize caption (stream already created with the original BM)
	//-initialize content (stream already created with the original BM)
	//2)Then format the box model into subregions (content/caption) given these settings.
	//  This will automatically account for min/max ranges
	//3)Record the visual extent of the box model
	//Do not support rotation or min/max at this time
	assert (oNode != null);
	assert (isBoxModelCompatible(oNode));

	//Initialize a box model from a
	if (! isBoxModelCompatible(oNode))
		return;

	//Read ne w/h, determine growability and if applicable the ne min/max ranges
	initWidthHeight(oNode, true);

	//Read anchor
	initAnchorPoint(oNode);

	//Read ne, content and caption margins
	initMargins(oNode);

	//Read border margins, content border margins
	initBorder(oNode);

	//Set the caption stream and rect
	initSplitCaption(oNode, /* oCaption, */ oCaptionRect);

	//Set the content stream and rect
	initSplitContent(oNode, /* oContent, */ oContentRect);

	//Format internals based on the info so far
	format(oNode);

	//Finally, determine the visual extent.
	initVisualExtent(oNode);
}

	private void initSplitContent(Element oNode, 
								//	AXTEDisplay oContent,
									Rect oRect /* = Rect.ZERO */) {
		if (! m_bHasCaption)
			return;
	// Javaport: not yet available. TODO
	//	m_oCaptionDisplay = oCaption;
		m_oCaptionDisplayExtent = oRect;
		assert (null != getCaption());
		assert (null != getCaptionDisplay());
	}

	private void initSplitCaption(Element oNode, 
								//	AXTEDisplay oCaption,
									Rect oRect /* = Rect.ZERO */) {
	// Javaport: not yet available. TODO
	//	m_oContentDisplay = oContent;
		m_oContentDisplayExtent = oRect;
		assert (null != getContent());
		assert (null != getContentDisplay());
	}
	
	/**
	 * Return true if this box model contains text that overflows it's allotted space
	 * and false otherwise.
	 * @return false if this object contains no text.
	 * @see #BoxModelLayout.hasOverflowingContentText()
	 * @exclude from published api.
	 */
	public boolean hasOverflowingContentText() {
		// Javaport: not yet available. TODO
		/*
		if ((m_oContentDisplay != null) && null != m_oContentDisplay.getDisplay()) {
			Rect rcContentRuntime = m_oContentDisplay.getDisplay().runtimeExtent();
			Rect rcContentAllotted = getContentExtent();
			if (m_bWasRotated && (m_oRotationAngle.degrees() == 90 || m_oRotationAngle.degrees() == 270)) {
				//Flip it - compare alloted-content width vs runtime h etc
				return rcContentAllotted.height() < rcContentRuntime.width()  || rcContentAllotted.width() < rcContentRuntime.height();
			}
			return rcContentAllotted.width() < rcContentRuntime.width()  || rcContentAllotted.height() < rcContentRuntime.height();
		}
		*/
		return false;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy