/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.layout.process;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.ElementAlignment;
import org.pentaho.reporting.engine.classic.core.layout.model.InlineRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.LayoutNodeTypes;
import org.pentaho.reporting.engine.classic.core.layout.model.PageGrid;
import org.pentaho.reporting.engine.classic.core.layout.model.ParagraphPoolBox;
import org.pentaho.reporting.engine.classic.core.layout.model.ParagraphRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderNode;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderableComplexText;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderableReplacedContentBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderableText;
import org.pentaho.reporting.engine.classic.core.layout.model.SpacerRenderNode;
import org.pentaho.reporting.engine.classic.core.layout.model.context.BoxDefinition;
import org.pentaho.reporting.engine.classic.core.layout.model.context.StaticBoxLayoutProperties;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorFeature;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.layout.process.alignment.CenterAlignmentProcessor;
import org.pentaho.reporting.engine.classic.core.layout.process.alignment.JustifyAlignmentProcessor;
import org.pentaho.reporting.engine.classic.core.layout.process.alignment.LastLineTextAlignmentProcessor;
import org.pentaho.reporting.engine.classic.core.layout.process.alignment.LeftAlignmentProcessor;
import org.pentaho.reporting.engine.classic.core.layout.process.alignment.RightAlignmentProcessor;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.DefaultSequenceList;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.EndSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.InlineBoxSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.InlineNodeSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.ReplacedContentSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.SequenceList;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.SpacerSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.StartSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.TextSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.util.CacheBoxShifter;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.AlignContext;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.BoxAlignContext;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.InlineBlockAlignContext;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.NodeAlignContext;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.ReplacedContentAlignContext;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.TextElementAlignContext;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.VerticalAlignmentProcessor;
import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility;
import org.pentaho.reporting.libraries.base.util.FastStack;
import org.pentaho.reporting.libraries.fonts.registry.FontMetrics;
/**
* This final processing step revalidates the text-layouting and the vertical alignment of block-level elements.
* <p/>
* At this point, the layout is almost finished, but non-dynamic text elements may contain more content on the last line
* than actually needed. This step recomputes the vertical alignment and merges all extra lines into the last line.
*
* @author Thomas Morgner
*/
public final class RevalidateAllAxisLayoutStep //extends IterateSimpleStructureProcessStep
{
private static class MergeContext
{
private RenderBox readContext;
private RenderBox writeContext;
protected MergeContext(final RenderBox writeContext, final RenderBox readContext)
{
this.readContext = readContext;
this.writeContext = writeContext;
}
public RenderBox getReadContext()
{
return readContext;
}
public RenderBox getWriteContext()
{
return writeContext;
}
}
private static final Log logger = LogFactory.getLog(RevalidateAllAxisLayoutStep.class);
private static final long OVERFLOW_DUMMY_WIDTH = StrictGeomUtility.toInternalValue(20000);
private LastLineTextAlignmentProcessor centerProcessor;
private LastLineTextAlignmentProcessor leftProcessor;
private LastLineTextAlignmentProcessor rightProcessor;
private LastLineTextAlignmentProcessor justifiedProcessor;
private PageGrid pageGrid;
private OutputProcessorMetaData metaData;
private VerticalAlignmentProcessor verticalAlignmentProcessor;
private boolean complexText;
private boolean strictTextProcessing;
public RevalidateAllAxisLayoutStep()
{
this.verticalAlignmentProcessor = new VerticalAlignmentProcessor();
}
public void initialize(final OutputProcessorMetaData metaData)
{
this.metaData = metaData;
complexText = metaData.isFeatureSupported(OutputProcessorFeature.COMPLEX_TEXT);
strictTextProcessing = metaData.isFeatureSupported(OutputProcessorFeature.STRICT_TEXT_PROCESSING);
}
public void processBoxChilds(final ParagraphRenderBox box, final PageGrid pageGrid)
{
try
{
this.pageGrid = pageGrid;
if (complexText)
{
processComplexText(box);
}
else
{
processSimpleText(box);
}
}
finally
{
this.pageGrid = null;
}
}
private void performVerticalBlockAlignment(final RenderBox box)
{
final RenderNode lastChildNode = box.getLastChild();
if (lastChildNode == null)
{
return;
}
final BoxDefinition boxDefinition = box.getBoxDefinition();
final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
final long insetBottom = blp.getBorderBottom() + boxDefinition.getPaddingBottom();
final long insetTop = blp.getBorderTop() + boxDefinition.getPaddingTop();
final long childY2 = lastChildNode.getCachedY() + lastChildNode.getCachedHeight() +
lastChildNode.getEffectiveMarginBottom();
final long childY1 = box.getFirstChild().getCachedY();
final long usedHeight = (childY2 - childY1);
final long computedHeight = box.getCachedHeight();
if (computedHeight > usedHeight)
{
// we have extra space to distribute. So lets shift some boxes.
final ElementAlignment valign = box.getNodeLayoutProperties().getVerticalAlignment();
if (ElementAlignment.BOTTOM.equals(valign))
{
final long boxBottom = (box.getCachedY() + box.getCachedHeight() - insetBottom);
final long delta = boxBottom - childY2;
CacheBoxShifter.shiftBoxChilds(box, delta);
}
else if (ElementAlignment.MIDDLE.equals(valign))
{
final long extraHeight = computedHeight - usedHeight;
final long boxTop = box.getCachedY() + insetTop + (extraHeight / 2);
final long delta = boxTop - childY1;
CacheBoxShifter.shiftBoxChilds(box, delta);
}
}
}
protected void processComplexText(final ParagraphRenderBox paragraph)
{
if (paragraph.getStaticBoxLayoutProperties().isOverflowY() == true)
{
performVerticalBlockAlignment(paragraph);
return;
}
final RenderNode lastLine = paragraph.getLastChild();
if (lastLine == null)
{
// Empty paragraph, no need to do anything ...
return;
}
// Process the direct childs of the paragraph
// Each direct child represents a line ..
final long paragraphBottom = paragraph.getCachedY() + paragraph.getCachedHeight();
if ((lastLine.getCachedY() + lastLine.getCachedHeight()) <= paragraphBottom)
{
// Already perfectly aligned.
return;
}
RenderNode node = paragraph.getFirstChild();
ParagraphPoolBox prev = null;
while (node != null)
{
// all childs of the linebox container must be inline boxes. They
// represent the lines in the paragraph. Any other element here is
// a error that must be reported
if (node.getNodeType() != LayoutNodeTypes.TYPE_BOX_LINEBOX)
{
throw new IllegalStateException("Encountered " + node.getClass());
}
// Process the current line.
final long y = node.getCachedY();
final long height = node.getCachedHeight();
if (y + height <= paragraphBottom)
{
// Node will fit, so we can allow it ..
prev = (ParagraphPoolBox) node;
node = node.getNext();
continue;
}
// Encountered a line that will not fully fit into the paragraph.
// Merge it with the previous line-paragraph.
if (prev == null)
{
// none of the lines fits fully, so get the first one at least
rebuildLastLineComplex((ParagraphPoolBox) node, (RenderBox) node.getNext());
node = node.getNext();
}
else
{
rebuildLastLineComplex(prev, (ParagraphPoolBox) node);
}
// now remove all pending lineboxes (they should be empty anyway).
while (node != null)
{
final RenderNode oldNode = node;
node = node.getNext();
paragraph.remove(oldNode);
}
return;
}
}
protected void processSimpleText(final ParagraphRenderBox paragraph)
{
if (paragraph.getStaticBoxLayoutProperties().isOverflowY() == true)
{
performVerticalBlockAlignment(paragraph);
return;
}
final RenderNode lastLine = paragraph.getLastChild();
if (lastLine == null)
{
// Empty paragraph, no need to do anything ...
return;
}
// Process the direct childs of the paragraph
// Each direct child represents a line ..
final long paragraphBottom = paragraph.getCachedY() + paragraph.getCachedHeight();
if ((lastLine.getCachedY() + lastLine.getCachedHeight()) <= paragraphBottom)
{
// Already perfectly aligned.
return;
}
final boolean overflowX = paragraph.getStaticBoxLayoutProperties().isOverflowX();
RenderNode node = paragraph.getFirstChild();
ParagraphPoolBox prev = null;
while (node != null)
{
// all childs of the linebox container must be inline boxes. They
// represent the lines in the paragraph. Any other element here is
// a error that must be reported
if (node.getNodeType() != LayoutNodeTypes.TYPE_BOX_LINEBOX)
{
throw new IllegalStateException("Encountered " + node.getClass());
}
final ParagraphPoolBox inlineRenderBox = (ParagraphPoolBox) node;
// Process the current line.
final long y = inlineRenderBox.getCachedY();
final long height = inlineRenderBox.getCachedHeight();
if (y + height <= paragraphBottom)
{
// Node will fit, so we can allow it ..
prev = inlineRenderBox;
node = node.getNext();
continue;
}
// Encountered a line that will not fully fit into the paragraph.
// Merge it with the previous line-paragraph.
final ParagraphPoolBox mergedLine = rebuildLastLine(prev, inlineRenderBox);
// now remove all pending lineboxes (they should be empty anyway).
while (node != null)
{
final RenderNode oldNode = node;
node = node.getNext();
paragraph.remove(oldNode);
}
if (mergedLine == null)
{
return;
}
final ElementAlignment textAlignment = paragraph.getLastLineAlignment();
final LastLineTextAlignmentProcessor proc = create(textAlignment);
// Now Build the sequence list that holds all nodes for the horizontal alignment computation.
// The last line will get a special "last-line" horizontal alignment. This is quite usefull if
// we are working with justified text and want the last line to be left-aligned.
final SequenceList sequenceList = createHorizontalSequenceList(mergedLine);
final long lineStart = paragraph.getContentAreaX1();
final long lineEnd;
if (overflowX)
{
lineEnd = OVERFLOW_DUMMY_WIDTH;
}
else
{
lineEnd = paragraph.getContentAreaX2();
}
if (lineEnd - lineStart <= 0)
{
final long minimumChunkWidth = paragraph.getMinimumChunkWidth();
proc.initialize(metaData, sequenceList, lineStart, lineStart + minimumChunkWidth, pageGrid, overflowX);
logger.warn("Auto-Corrected zero-width linebox."); // NON-NLS
}
else
{
proc.initialize(metaData, sequenceList, lineStart, lineEnd, pageGrid, overflowX);
}
proc.performLastLineAlignment();
proc.deinitialize();
// Now Perform the vertical layout for the last line of the paragraph.
final BoxAlignContext valignContext = createVerticalAlignContext(mergedLine);
final StaticBoxLayoutProperties blp = mergedLine.getStaticBoxLayoutProperties();
final BoxDefinition bdef = mergedLine.getBoxDefinition();
final long insetTop = (blp.getBorderTop() + bdef.getPaddingTop());
final long contentAreaY1 = mergedLine.getCachedY() + insetTop;
final long lineHeight = mergedLine.getLineHeight();
verticalAlignmentProcessor.align(valignContext, contentAreaY1, lineHeight);
// And finally make sure that the paragraph box itself obeys to the defined vertical box alignment.
performVerticalBlockAlignment(paragraph);
return;
}
}
private BoxAlignContext createVerticalAlignContext(final InlineRenderBox box)
{
BoxAlignContext alignContext = new BoxAlignContext(box);
final FastStack<RenderBox> contextStack = new FastStack<RenderBox>(50);
final FastStack<AlignContext> alignContextStack = new FastStack<AlignContext>(50);
RenderNode next = box.getFirstChild();
RenderBox context = box;
while (next != null)
{
// process next
final int nodeType = next.getLayoutNodeType();
if ((nodeType & LayoutNodeTypes.MASK_BOX_INLINE) == LayoutNodeTypes.MASK_BOX_INLINE)
{
final RenderBox nBox = (RenderBox) next;
final RenderNode firstChild = nBox.getFirstChild();
if (firstChild != null)
{
// Open a non-empty box context
contextStack.push(context);
alignContextStack.push(alignContext);
next = firstChild;
final BoxAlignContext childBoxContext = new BoxAlignContext(nBox);
alignContext.addChild(childBoxContext);
context = nBox;
alignContext = childBoxContext;
}
else
{
// Process an empty box.
final BoxAlignContext childBoxContext = new BoxAlignContext(nBox);
alignContext.addChild(childBoxContext);
next = nBox.getNext();
}
}
else
{
// Process an ordinary node.
if (nodeType == LayoutNodeTypes.TYPE_NODE_TEXT)
{
alignContext.addChild(new TextElementAlignContext((RenderableText) next));
}
else if (nodeType == LayoutNodeTypes.TYPE_BOX_CONTENT)
{
alignContext.addChild(new ReplacedContentAlignContext((RenderableReplacedContentBox) next, 0));
}
else if ((nodeType & LayoutNodeTypes.MASK_BOX_BLOCK) == LayoutNodeTypes.MASK_BOX_BLOCK)
{
alignContext.addChild(new InlineBlockAlignContext((RenderBox) next));
}
else
{
alignContext.addChild(new NodeAlignContext(next));
}
next = next.getNext();
}
while (next == null && contextStack.isEmpty() == false)
{
// Finish the current box context, if needed
next = context.getNext();
context = contextStack.pop();
alignContext.validate();
alignContext = (BoxAlignContext) alignContextStack.pop();
}
}
return alignContext;
}
private SequenceList createHorizontalSequenceList(final InlineRenderBox box)
{
final SequenceList sequenceList = new DefaultSequenceList();
sequenceList.add(StartSequenceElement.INSTANCE, box);
RenderNode next = box.getFirstChild();
RenderBox context = box;
final FastStack<RenderBox> contextStack = new FastStack<RenderBox>(50);
boolean containsContent = false;
while (next != null)
{
// process next
final int nodeType = next.getLayoutNodeType();
if ((nodeType & LayoutNodeTypes.MASK_BOX_INLINE) == LayoutNodeTypes.MASK_BOX_INLINE)
{
final RenderBox nBox = (RenderBox) next;
final RenderNode firstChild = nBox.getFirstChild();
if (firstChild != null)
{
// Open a non-empty box context
contextStack.push(context);
next = firstChild;
sequenceList.add(StartSequenceElement.INSTANCE, nBox);
context = nBox;
}
else
{
// Process an empty box.
sequenceList.add(StartSequenceElement.INSTANCE, nBox);
sequenceList.add(EndSequenceElement.INSTANCE, nBox);
next = nBox.getNext();
}
}
else
{
// Process an ordinary node.
if (nodeType == LayoutNodeTypes.TYPE_NODE_TEXT)
{
sequenceList.add(TextSequenceElement.INSTANCE, next);
containsContent = true;
}
else if (nodeType == LayoutNodeTypes.TYPE_BOX_CONTENT)
{
sequenceList.add(ReplacedContentSequenceElement.INSTANCE, next);
containsContent = true;
}
else if (nodeType == LayoutNodeTypes.TYPE_NODE_SPACER)
{
if (containsContent)
{
sequenceList.add(SpacerSequenceElement.INSTANCE, next);
}
}
else if ((nodeType & LayoutNodeTypes.MASK_BOX_BLOCK) == LayoutNodeTypes.MASK_BOX_BLOCK)
{
containsContent = true;
sequenceList.add(InlineBoxSequenceElement.INSTANCE, next);
}
else
{
containsContent = true;
sequenceList.add(InlineNodeSequenceElement.INSTANCE, next);
}
next = next.getNext();
}
while (next == null && contextStack.isEmpty() == false)
{
// Finish the current box context, if needed
sequenceList.add(EndSequenceElement.INSTANCE, context);
next = context.getNext();
context = contextStack.pop();
}
}
sequenceList.add(EndSequenceElement.INSTANCE, box);
return sequenceList;
}
private LastLineTextAlignmentProcessor create(final ElementAlignment alignment)
{
if (ElementAlignment.CENTER.equals(alignment))
{
if (centerProcessor == null)
{
centerProcessor = new CenterAlignmentProcessor();
}
return centerProcessor;
}
else if (ElementAlignment.RIGHT.equals(alignment))
{
if (rightProcessor == null)
{
rightProcessor = new RightAlignmentProcessor();
}
return rightProcessor;
}
else if (ElementAlignment.JUSTIFY.equals(alignment))
{
if (justifiedProcessor == null)
{
justifiedProcessor = new JustifyAlignmentProcessor();
}
return justifiedProcessor;
}
if (leftProcessor == null)
{
leftProcessor = new LeftAlignmentProcessor();
}
return leftProcessor;
}
private ParagraphPoolBox rebuildLastLine(final ParagraphPoolBox lineBox,
final ParagraphPoolBox nextBox)
{
if (lineBox == null)
{
if (nextBox == null)
{
throw new NullPointerException("Both Line- and Next-Line are null.");
}
return rebuildLastLine(nextBox, (ParagraphPoolBox) nextBox.getNext());
}
if (nextBox == null || strictTextProcessing)
{
// Linebox is finished, no need to do any merging anymore..
return lineBox;
}
boolean needToAddSpacing = true;
// do the merging ..
final FastStack<MergeContext> contextStack = new FastStack<MergeContext>(50);
RenderNode next = nextBox.getFirstChild();
MergeContext context = new MergeContext(lineBox, nextBox);
while (next != null)
{
// process next
final RenderBox writeContext = context.getWriteContext();
final StaticBoxLayoutProperties staticBoxLayoutProperties = writeContext.getStaticBoxLayoutProperties();
long spaceWidth = staticBoxLayoutProperties.getSpaceWidth();
if (spaceWidth == 0)
{
// Space has not been computed yet.
final FontMetrics fontMetrics = metaData.getFontMetrics(writeContext.getStyleSheet());
spaceWidth = StrictGeomUtility.fromFontMetricsValue(fontMetrics.getCharWidth(' '));
staticBoxLayoutProperties.setSpaceWidth(spaceWidth);
}
if (next.isRenderBox())
{
final RenderBox nBox = (RenderBox) next;
final RenderNode firstChild = nBox.getFirstChild();
if (firstChild != null)
{
contextStack.push(context);
next = firstChild;
final RenderNode writeContextLastChild = writeContext.getLastChild();
if (writeContextLastChild.isRenderBox())
{
if (writeContextLastChild.getInstanceId() == nBox.getInstanceId())
{
context = new MergeContext((RenderBox) writeContextLastChild, nBox);
}
else
{
if (needToAddSpacing)
{
if (spaceWidth > 0)
{
// docmark: Used zero as new height
final SpacerRenderNode spacer = new SpacerRenderNode(spaceWidth, 0, false, 1);
spacer.setVirtualNode(true);
writeContext.addGeneratedChild(spacer);
}
needToAddSpacing = false;
}
final RenderBox newWriter = (RenderBox) nBox.derive(false);
newWriter.setVirtualNode(true);
writeContext.addGeneratedChild(newWriter);
context = new MergeContext(newWriter, nBox);
}
}
else
{
if (needToAddSpacing)
{
if (spaceWidth > 0)
{
// docmark: Used zero as new height
final SpacerRenderNode spacer = new SpacerRenderNode(spaceWidth, 0, false, 1);
spacer.setVirtualNode(true);
writeContext.addGeneratedChild(spacer);
}
needToAddSpacing = false;
}
final RenderBox newWriter = (RenderBox) nBox.derive(false);
newWriter.setVirtualNode(true);
writeContext.addGeneratedChild(newWriter);
context = new MergeContext(newWriter, nBox);
}
}
else
{
if (needToAddSpacing)
{
if (spaceWidth > 0)
{
// docmark: Used zero as new height
final SpacerRenderNode spacer = new SpacerRenderNode(spaceWidth, 0, false, 1);
spacer.setVirtualNode(true);
writeContext.addGeneratedChild(spacer);
}
needToAddSpacing = false;
}
final RenderNode box = nBox.derive(true);
box.setVirtualNode(true);
writeContext.addGeneratedChild(box);
next = nBox.getNext();
}
}
else
{
if (needToAddSpacing)
{
final RenderNode lastChild = writeContext.getLastChild();
if (spaceWidth > 0 && lastChild != null &&
(lastChild.getNodeType() != LayoutNodeTypes.TYPE_NODE_SPACER))
{
// docmark: Used zero as new height
final SpacerRenderNode spacer = new SpacerRenderNode(spaceWidth, 0, false, 1);
spacer.setVirtualNode(true);
writeContext.addGeneratedChild(spacer);
}
needToAddSpacing = false;
}
final RenderNode child = next.derive(true);
child.setVirtualNode(true);
writeContext.addGeneratedChild(child);
next = next.getNext();
}
while (next == null && contextStack.isEmpty() == false)
{
// Log.debug ("FINISH " + context.getReadContext());
next = context.getReadContext().getNext();
context = contextStack.pop();
}
}
return rebuildLastLine(lineBox, (ParagraphPoolBox) nextBox.getNext());
}
private RenderBox rebuildLastLineComplex(final RenderBox lineBox,
final RenderBox nextBox)
{
if (lineBox == null)
{
throw new NullPointerException();
}
if (nextBox == null)
{
return lineBox;
}
RenderNode child = nextBox.getFirstChild();
while (child != null)
{
if (child.isRenderBox())
{
if (lineBox.getLastChild().isRenderBox() &&
lineBox.getLastChild().getInstanceId() == child.getInstanceId())
{
rebuildLastLineComplex((RenderBox) lineBox.getLastChild(), (RenderBox) child);
}
else
{
RenderBox lineBoxChild = (RenderBox) child.derive(false);
rebuildLastLineComplex(lineBoxChild, (RenderBox) child);
lineBoxChild.close();
lineBox.addGeneratedChild(lineBoxChild);
}
}
else if (child instanceof RenderableComplexText)
{
RenderableComplexText childAsText = (RenderableComplexText) child;
RenderNode n = lineBox.getLastChild();
if (n instanceof RenderableComplexText)
{
RenderableComplexText lastLine = (RenderableComplexText) n;
if (lastLine.isSameSource(childAsText))
{
lineBox.replaceChild(n, lastLine.merge(childAsText));
}
else
{
lineBox.addGeneratedChild(child);
}
}
else
{
lineBox.addGeneratedChild(child);
}
}
else
{
lineBox.addGeneratedChild(child);
}
child = child.getNext();
}
return null;
}
}