package org.pentaho.reporting.engine.classic.core.layout.model;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.BreakIterator;
import org.pentaho.reporting.engine.classic.core.ReportAttributeMap;
import org.pentaho.reporting.engine.classic.core.layout.model.context.NodeLayoutProperties;
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.output.RenderUtility;
import org.pentaho.reporting.engine.classic.core.layout.process.text.ParagraphFontMetrics;
import org.pentaho.reporting.engine.classic.core.layout.process.text.RichTextSpec;
import org.pentaho.reporting.engine.classic.core.layout.process.text.RichTextSpecProducer;
import org.pentaho.reporting.engine.classic.core.metadata.ElementType;
import org.pentaho.reporting.engine.classic.core.style.StyleSheet;
import org.pentaho.reporting.engine.classic.core.style.TextStyleKeys;
import org.pentaho.reporting.engine.classic.core.util.InstanceID;
import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility;
import org.pentaho.reporting.libraries.base.util.DebugLog;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
public class RenderableComplexText extends RenderNode
{
private RichTextSpec richText;
private int start;
private int end;
private String text;
private TextLayout textLayout;
private boolean forceLinebreak;
private ParagraphFontMetrics paragraphFontMetrics;
public RenderableComplexText(final StyleSheet styleSheet,
final InstanceID instanceID,
final ElementType elementType,
final ReportAttributeMap<Object> attributes,
final RichTextSpec text)
{
super(new NodeLayoutProperties(styleSheet, attributes, instanceID, elementType));
this.text = text.getText();
this.richText = text;
this.forceLinebreak = false;
this.start = 0;
this.end = text.length();
}
public RenderableComplexText(final StyleSheet styleSheet,
final InstanceID instanceID,
final ElementType elementType,
final ReportAttributeMap<Object> attributes,
final String text)
{
super(new NodeLayoutProperties(styleSheet, attributes, instanceID, elementType));
this.text = text;
this.richText = null;
this.forceLinebreak = false;
this.start = 0;
this.end = text.length();
}
public void computeMinimumChunkWidth(final OutputProcessorMetaData data,
final ResourceManager resourceManager)
{
if (getMinimumChunkWidth() != 0)
{
return;
}
if (data.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY) == false &&
getStyleSheet().getBooleanStyleProperty(TextStyleKeys.WORDBREAK) == false)
{
return;
}
long minimumChunkWidth = 0;
BreakIterator wordInstance = BreakIterator.getWordInstance();
wordInstance.setText(text);
final boolean antiAliasing = RenderUtility.isFontSmooth(getStyleSheet(), data);
final FontRenderContext fontRenderContext = new FontRenderContext(null, antiAliasing, true);
int start = wordInstance.first();
for (int end = wordInstance.next(); end != BreakIterator.DONE; start = end, end = wordInstance.next())
{
String word = text.substring(start, end);
AttributedCharacterIterator attributedCharacterIterator =
new RichTextSpecProducer(data, resourceManager).computeText(this, word).createAttributedCharacterIterator();
TextLayout t = new TextLayout(attributedCharacterIterator, fontRenderContext);
double width = t.getVisibleAdvance();
final long wordMinChunkWidth = StrictGeomUtility.toInternalValue(width);
minimumChunkWidth = Math.max(minimumChunkWidth, wordMinChunkWidth);
}
setMinimumChunkWidth(minimumChunkWidth);
}
public int getNodeType()
{
return LayoutNodeTypes.TYPE_NODE_COMPLEX_TEXT;
}
public String getRawText()
{
return text;
}
public RichTextSpec getRichText()
{
if (richText == null)
{
// code-smell - we probably should introduce a "ProcessedComplexText" type to distinguish between
// raw text and text that has been processed by the CanvasMinor-step.
throw new IllegalStateException("Calling 'getRichText' is only valid after layouting is complete.");
}
return richText.substring(start, end);
}
public TextLayout getTextLayout()
{
return textLayout;
}
public void setTextLayout(final TextLayout textLayout)
{
this.textLayout = textLayout;
}
public void setForceLinebreak(final boolean forceLinebreak)
{
this.forceLinebreak = forceLinebreak;
}
public boolean isForceLinebreak()
{
return forceLinebreak;
}
public RenderableComplexText merge(final RenderableComplexText suffix)
{
if (richText != suffix.richText)
{
throw new IllegalStateException("Not from the same source");
}
final RenderableComplexText text = (RenderableComplexText) derive(true);
text.end = suffix.end;
text.setMinimumChunkWidth(Math.max (getMinimumChunkWidth(), suffix.getMinimumChunkWidth()));
return text;
}
public boolean isSameSource(final RenderableComplexText suffix)
{
if (richText != suffix.richText)
{
return false;
}
return true;
}
public void setParagraphFontMetrics(final ParagraphFontMetrics paragraphFontMetrics)
{
this.paragraphFontMetrics = paragraphFontMetrics;
}
public ParagraphFontMetrics getParagraphFontMetrics()
{
return paragraphFontMetrics;
}
}