Package net.sf.jasperreports.engine.fill

Source Code of net.sf.jasperreports.engine.fill.TextMeasurer$TextMeasuredState

/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2009 Jaspersoft Corporation. All rights reserved.
* http://www.jaspersoft.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JasperReports 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.jasperreports.engine.fill;

import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.AttributedString;
import java.text.BreakIterator;
import java.text.CharacterIterator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import net.sf.jasperreports.engine.JRCommonText;
import net.sf.jasperreports.engine.JRParagraph;
import net.sf.jasperreports.engine.JRPrintText;
import net.sf.jasperreports.engine.JRPropertiesHolder;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRTextElement;
import net.sf.jasperreports.engine.TabStop;
import net.sf.jasperreports.engine.export.AbstractTextRenderer;
import net.sf.jasperreports.engine.export.AwtTextRenderer;
import net.sf.jasperreports.engine.util.DelegatePropertiesHolder;
import net.sf.jasperreports.engine.util.JRProperties;
import net.sf.jasperreports.engine.util.JRStringUtil;
import net.sf.jasperreports.engine.util.JRStyledText;
import net.sf.jasperreports.engine.util.ParagraphUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
* Default text measurer implementation.
*
* @author Teodor Danciu (teodord@users.sourceforge.net)
* @version $Id: TextMeasurer.java 4298 2011-04-18 16:51:58Z teodord $
*/
public class TextMeasurer implements JRTextMeasurer
{
  private static final Log log = LogFactory.getLog(TextMeasurer.class);

  private JRCommonText textElement;
  private JRPropertiesHolder propertiesHolder;

  /**
   *
   */
  //private MaxFontSizeFinder maxFontSizeFinder;
 
  private int width;
  private int height;
  private int topPadding;
  private int leftPadding;
  private int bottomPadding;
  private int rightPadding;
  private JRParagraph jrParagraph;

  //private float formatWidth;
  private int maxHeight;
  private boolean canOverflow;
  private Map globalAttributes;
  private TextMeasuredState measuredState;
  private TextMeasuredState prevMeasuredState;
 
  protected static class TextMeasuredState implements JRMeasuredText, Cloneable
  {
    private final boolean saveLineBreakOffsets;
   
    protected int textOffset;
    protected int lines;
    protected int paragraphStartLine;
    protected float textHeight;
    protected boolean isLeftToRight = true;
    protected String textSuffix;
   
    protected int lastOffset;
    protected ArrayList lineBreakOffsets;
   
    public TextMeasuredState(boolean saveLineBreakOffsets)
    {
      this.saveLineBreakOffsets = saveLineBreakOffsets;
    }
   
    public boolean isLeftToRight()
    {
      return isLeftToRight;
    }
   
    public int getTextOffset()
    {
      return textOffset;
    }
   
    public float getTextHeight()
    {
      return textHeight;
    }
   
    public String getTextSuffix()
    {
      return textSuffix;
    }
   
    public TextMeasuredState cloneState()
    {
      try
      {
        TextMeasuredState clone = (TextMeasuredState) super.clone();
       
        //clone the list of offsets
        //might be a performance problem on very large texts
        if (lineBreakOffsets != null)
        {
          clone.lineBreakOffsets = (ArrayList) lineBreakOffsets.clone();
        }
       
        return clone;
      }
      catch (CloneNotSupportedException e)
      {
        //never
        throw new JRRuntimeException(e);
      }
    }

    protected void addLineBreak()
    {
      if (saveLineBreakOffsets)
      {
        if (lineBreakOffsets == null)
        {
          lineBreakOffsets = new ArrayList();
        }

        int breakOffset = textOffset - lastOffset;
        lineBreakOffsets.add(Integer.valueOf(breakOffset));
        lastOffset = textOffset;
      }
    }
   
    public short[] getLineBreakOffsets()
    {
      if (!saveLineBreakOffsets)
      {
        //if no line breaks are to be saved, return null
        return null;
      }
     
      //if the last line break occurred at the truncation position
      //exclude the last break offset
      int exclude = lastOffset == textOffset ? 1 : 0;
      if (lineBreakOffsets == null
          || lineBreakOffsets.size() <= exclude)
      {
        //use the zero length array singleton
        return JRPrintText.ZERO_LINE_BREAK_OFFSETS;
      }
     
      short[] offsets = new short[lineBreakOffsets.size() - exclude];
      boolean overflow = false;
      for (int i = 0; i < offsets.length; i++)
      {
        int offset = ((Integer) lineBreakOffsets.get(i)).intValue();
        if (offset > Short.MAX_VALUE)
        {
          if (log.isWarnEnabled())
          {
            log.warn("Line break offset value " + offset
                + " is bigger than the maximum supported value of"
                + Short.MAX_VALUE
                + ". Line break offsets will not be saved for this text.");
          }
         
          overflow = true;
          break;
        }
        offsets[i] = (short) offset;
      }
     
      if (overflow)
      {
        //if a line break offset overflow occurred, do not return any
        //line break offsets
        return null;
      }
     
      return offsets;
    }
  }
 
  /**
   *
   */
  public TextMeasurer(JRCommonText textElement)
  {
    this.textElement = textElement;
    this.propertiesHolder = textElement instanceof JRPropertiesHolder ? (JRPropertiesHolder) textElement : null;//FIXMENOW all elements are now properties holders, so interfaces might be rearranged
    if (textElement.getDefaultStyleProvider() instanceof JRPropertiesHolder)
    {
      this.propertiesHolder =
        new DelegatePropertiesHolder(
          propertiesHolder,
          (JRPropertiesHolder)textElement.getDefaultStyleProvider()
          );
    }
  }
 
  /**
   *
   */
  protected void initialize(
    JRStyledText styledText,
    int remainingTextStart,
    int availableStretchHeight,
    boolean canOverflow
    )
  {
    width = textElement.getWidth();
    height = textElement.getHeight();
   
    topPadding = textElement.getLineBox().getTopPadding().intValue();
    leftPadding = textElement.getLineBox().getLeftPadding().intValue();
    bottomPadding = textElement.getLineBox().getBottomPadding().intValue();
    rightPadding = textElement.getLineBox().getRightPadding().intValue();
   
    jrParagraph = textElement.getParagraph();

    switch (textElement.getRotationValue())
    {
      case LEFT :
      {
        width = textElement.getHeight();
        height = textElement.getWidth();
        int tmpPadding = topPadding;
        topPadding = leftPadding;
        leftPadding = bottomPadding;
        bottomPadding = rightPadding;
        rightPadding = tmpPadding;
        break;
      }
      case RIGHT :
      {
        width = textElement.getHeight();
        height = textElement.getWidth();
        int tmpPadding = topPadding;
        topPadding = rightPadding;
        rightPadding = bottomPadding;
        bottomPadding = leftPadding;
        leftPadding = tmpPadding;
        break;
      }
      case UPSIDE_DOWN :
      {
        int tmpPadding = topPadding;
        topPadding = bottomPadding;
        bottomPadding = tmpPadding;
        tmpPadding = leftPadding;
        leftPadding = rightPadding;
        rightPadding = tmpPadding;
        break;
      }
      case NONE :
      default :
      {
      }
    }
   
    //maxFontSizeFinder = MaxFontSizeFinder.getInstance(!JRCommonText.MARKUP_NONE.equals(textElement.getMarkup()));

//    formatWidth = width - leftPadding - rightPadding;
//    formatWidth = formatWidth < 0 ? 0 : formatWidth;
    maxHeight = height + availableStretchHeight - topPadding - bottomPadding;
    maxHeight = maxHeight < 0 ? 0 : maxHeight;
    this.canOverflow = canOverflow;
    this.globalAttributes = styledText.getGlobalAttributes();
   
    boolean saveLineBreakOffsets = JRProperties.getBooleanProperty(propertiesHolder,
        JRTextElement.PROPERTY_SAVE_LINE_BREAKS, false);
   
    measuredState = new TextMeasuredState(saveLineBreakOffsets);
    measuredState.lastOffset = remainingTextStart;
    prevMeasuredState = null;
   
  }

  /**
   *
   */
  public JRMeasuredText measure(
    JRStyledText styledText,
    int remainingTextStart,
    int availableStretchHeight,
    boolean canOverflow
    )
  {
    /*   */
    initialize(styledText, remainingTextStart, availableStretchHeight, canOverflow);

    AttributedCharacterIterator allParagraphs =
      styledText.getAwtAttributedString(
        JRProperties.getBooleanProperty(propertiesHolder, JRStyledText.PROPERTY_AWT_IGNORE_MISSING_FONT, false)
        ).getIterator();

    int tokenPosition = remainingTextStart;
    int lastParagraphStart = remainingTextStart;
    String lastParagraphText = null;

    String remainingText = styledText.getText().substring(remainingTextStart);
    StringTokenizer tkzer = new StringTokenizer(remainingText, "\n", true);

    boolean rendered = true;
    // text is split into paragraphs, using the newline character as delimiter
    while(tkzer.hasMoreTokens() && rendered)
    {
      String token = tkzer.nextToken();

      if ("\n".equals(token))
      {
        rendered = renderParagraph(allParagraphs, lastParagraphStart, lastParagraphText);

        lastParagraphStart = tokenPosition + (tkzer.hasMoreTokens() || tokenPosition == 0 ? 1 : 0);
        lastParagraphText = null;
      }
      else
      {
        lastParagraphStart = tokenPosition;
        lastParagraphText = token;
      }

      tokenPosition += token.length();
    }

    if (rendered && lastParagraphStart < remainingTextStart + remainingText.length())
    {
      renderParagraph(allParagraphs, lastParagraphStart, lastParagraphText);
    }
   
    return measuredState;
  }

  /**
   *
   */
  protected boolean renderParagraph(
    AttributedCharacterIterator allParagraphs,
    int lastParagraphStart,
    String lastParagraphText
    )
  {
    AttributedCharacterIterator paragraph = null;
   
    if (lastParagraphText == null)
    {
      paragraph =
        new AttributedString(
          " ",
          new AttributedString(
            allParagraphs,
            lastParagraphStart,
            lastParagraphStart + 1
            ).getIterator().getAttributes()
          ).getIterator();
    }
    else
    {
      paragraph =
        new AttributedString(
          allParagraphs,
          lastParagraphStart,
          lastParagraphStart + lastParagraphText.length()
          ).getIterator();
    }

    List<Integer> tabIndexes = JRStringUtil.getTabIndexes(lastParagraphText);
   
    int[] currentTabHolder = new int[]{0};
    TabStop[] nextTabStopHolder = new TabStop[]{null};
    boolean[] requireNextWordHolder = new boolean[]{false};

    LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, getFontRenderContext());
   
    measuredState.paragraphStartLine = measuredState.lines;
    measuredState.textOffset = lastParagraphStart;
   
    boolean rendered = true;
    boolean renderedLine = false;

    // the paragraph is measured one line at a time
    while (lineMeasurer.getPosition() < paragraph.getEndIndex() && rendered)
    {
      rendered = renderNextLine(lineMeasurer, paragraph, tabIndexes, currentTabHolder, nextTabStopHolder, requireNextWordHolder);
      renderedLine = renderedLine || rendered;
    }
   
    //if we rendered at least one line, and the last line didn't fit
    //and the text does not overflow
    if (!rendered && prevMeasuredState != null && !canOverflow)
    {
      //handle last rendered row
      processLastTruncatedRow(allParagraphs, lastParagraphText, lastParagraphStart, renderedLine);
    }
   
    return rendered;
  }
 
  protected void processLastTruncatedRow(
    AttributedCharacterIterator allParagraphs,
    String paragraphText,
    int paragraphOffset,
    boolean lineTruncated
    )
  {
    if (lineTruncated && isToTruncateAtChar())
    {
      truncateLastLineAtChar(allParagraphs, paragraphText, paragraphOffset);
    }
   
    appendTruncateSuffix(allParagraphs);
  }

  protected void truncateLastLineAtChar(
    AttributedCharacterIterator allParagraphs,
    String paragraphText,
    int paragraphOffset
    )
  {
    //truncate the original line at char
    measuredState = prevMeasuredState.cloneState();
    AttributedCharacterIterator lineParagraph =
      new AttributedString(
        allParagraphs,
        measuredState.textOffset,
        paragraphOffset + paragraphText.length()
        ).getIterator();
    LineBreakMeasurer lineMeasurer =
      new LineBreakMeasurer(
        lineParagraph,
        BreakIterator.getCharacterInstance(),
        getFontRenderContext()
        );
    //render again the last line
    //if the line does not fit now, it will remain empty
    renderNextLine(lineMeasurer, lineParagraph, null, new int[]{0}, new TabStop[]{null}, new boolean[]{false});
  }

  protected void appendTruncateSuffix(AttributedCharacterIterator allParagraphs)
  {
    String truncateSuffx = getTruncateSuffix();
    if (truncateSuffx == null)
    {
      return;
    }
   
    int lineStart = prevMeasuredState.textOffset;

    //advance from the line start until the next line start or the first newline
    StringBuffer lineText = new StringBuffer();
    allParagraphs.setIndex(lineStart);
    while (allParagraphs.getIndex() < measuredState.textOffset
        && allParagraphs.current() != '\n')
    {
      lineText.append(allParagraphs.current());
      allParagraphs.next();
    }
    int linePosition = allParagraphs.getIndex() - lineStart;
   
    //iterate to the beginning of the line
    boolean done = false;
    do
    {
      measuredState = prevMeasuredState.cloneState();

      String text = lineText.substring(0, linePosition) + truncateSuffx;
      AttributedString attributedText = new AttributedString(text);
     
      //set original attributes for the text part
      AttributedCharacterIterator lineAttributes =
        new AttributedString(
          allParagraphs,
          measuredState.textOffset,
          measuredState.textOffset + linePosition
          ).getIterator();
      setAttributes(attributedText, lineAttributes, 0);
     
      //set global attributes for the suffix part
      setAttributes(
        attributedText,
        globalAttributes,
        text.length() - truncateSuffx.length(),
        text.length()
        );
     
      AttributedCharacterIterator lineParagraph = attributedText.getIterator();
     
      BreakIterator breakIterator =
        isToTruncateAtChar()
        ? BreakIterator.getCharacterInstance()
        : BreakIterator.getLineInstance();
      LineBreakMeasurer lineMeasurer =
        new LineBreakMeasurer(
          lineParagraph,
          breakIterator,
          getFontRenderContext()
          );

      if (renderNextLine(lineMeasurer, lineParagraph, null, new int[]{0}, new TabStop[]{null}, new boolean[]{false}))
      {
        int lastPos = lineMeasurer.getPosition();
        //test if the entire suffix fit
        if (lastPos == linePosition + truncateSuffx.length())
        {
          //subtract the suffix from the offset
          measuredState.textOffset -= truncateSuffx.length();
          measuredState.textSuffix = truncateSuffx;
          done = true;
        }
        else
        {
          linePosition = breakIterator.preceding(linePosition);
          if (linePosition == BreakIterator.DONE)
          {
            //if the text suffix did not fit the line, only the part of it that fits will show

            //truncate the suffix
            String actualSuffix = truncateSuffx.substring(0,
                measuredState.textOffset - prevMeasuredState.textOffset);
            //if the last text char is not a new line
            if (prevMeasuredState.textOffset > 0
                && allParagraphs.setIndex(prevMeasuredState.textOffset - 1) != '\n')
            {
              //force a new line so that the suffix is displayed on the last line
              actualSuffix = '\n' + actualSuffix;
            }
            measuredState.textSuffix = actualSuffix;
           
            //restore the next to last line offset
            measuredState.textOffset = prevMeasuredState.textOffset;

            done = true;
          }
        }
      }
      else
      {
        //if the line did not fit, leave it empty
        done = true;
      }
    }
    while (!done);
  }

  protected boolean isToTruncateAtChar()
  {
    return JRProperties.getBooleanProperty(propertiesHolder,
        JRTextElement.PROPERTY_TRUNCATE_AT_CHAR, false);
  }

  protected String getTruncateSuffix()
  {
    String truncateSuffx = JRProperties.getProperty(propertiesHolder,
        JRTextElement.PROPERTY_TRUNCATE_SUFFIX);
    if (truncateSuffx != null)
    {
      truncateSuffx = truncateSuffx.trim();
      if (truncateSuffx.length() == 0)
      {
        truncateSuffx = null;
      }
    }
    return truncateSuffx;
  }
 
  protected boolean renderNextLine(LineBreakMeasurer lineMeasurer, AttributedCharacterIterator paragraph, List<Integer> tabIndexes, int[] currentTabHolder, TabStop[] nextTabStopHolder, boolean[] requireNextWordHolder)
  {
    boolean lineComplete = false;

    int lineStartPosition = lineMeasurer.getPosition();
   
    float maxAscent = 0;
    float maxDescent = 0;
    float maxLeading = 0;
    int characterCount = 0;
    boolean isLeftToRight = true;
   
    // each line is split into segments, using the tab character as delimiter
    List<TabSegment> segments = new ArrayList<TabSegment>(1);

    TabSegment oldSegment = null;
    TabSegment crtSegment = null;

    // splitting the current line into tab segments
    while (!lineComplete)
    {
      // the current segment limit is either the next tab character or the paragraph end
      int tabIndexOrEndIndex = (tabIndexes == null || currentTabHolder[0] >= tabIndexes.size() ? paragraph.getEndIndex() : tabIndexes.get(currentTabHolder[0]) + 1);
     
      float startX = (lineMeasurer.getPosition() == 0 ? textElement.getParagraph().getFirstLineIndent() : 0);
      float endX = width - textElement.getParagraph().getRightIndent() - rightPadding;
      endX = endX < startX ? startX : endX;
      //formatWidth = endX - startX;
      //formatWidth = endX;

      int startIndex = lineMeasurer.getPosition();

      int rightX = 0;

      if (segments.size() == 0)
      {
        rightX = (int)startX;
        //nextTabStop = nextTabStop;
      }
      else
      {
        rightX = oldSegment.rightX;
        nextTabStopHolder[0] = ParagraphUtil.getNextTabStop(jrParagraph, endX, rightX);
      }

      //float availableWidth = formatWidth - ParagraphUtil.getSegmentOffset(nextTabStopHolder[0], rightX); // nextTabStop can be null here; and that's OK
      float availableWidth = endX - textElement.getParagraph().getLeftIndent() - ParagraphUtil.getSegmentOffset(nextTabStopHolder[0], rightX); // nextTabStop can be null here; and that's OK
     
      // creating a text layout object for each tab segment
      TextLayout layout =
        lineMeasurer.nextLayout(
          availableWidth,
          tabIndexOrEndIndex,
          requireNextWordHolder[0]
          );
     
      if (layout != null)
      {
        maxAscent = Math.max(maxAscent, layout.getAscent());
        maxDescent = Math.max(maxDescent, layout.getDescent());
        maxLeading = Math.max(maxLeading, layout.getLeading());
        characterCount += layout.getCharacterCount();
        isLeftToRight = isLeftToRight && layout.isLeftToRight();

        //creating the current segment
        crtSegment = new TabSegment();
        crtSegment.layout = layout;

        int leftX = ParagraphUtil.getLeftX(nextTabStopHolder[0], layout.getAdvance()); // nextTabStop can be null here; and that's OK
        if (rightX > leftX)
        {
          crtSegment.leftX = rightX;
          crtSegment.rightX = (int)(rightX + layout.getAdvance());//FIXMETAB some rounding issues here
        }
        else
        {
          crtSegment.leftX = leftX;
          // we need this special tab stop based utility call because adding the advance to leftX causes rounding issues
          crtSegment.rightX = ParagraphUtil.getRightX(nextTabStopHolder[0], layout.getAdvance()); // nextTabStop can be null here; and that's OK
        }

        segments.add(crtSegment);
      }
     
      requireNextWordHolder[0] = true;

      if (lineMeasurer.getPosition() == tabIndexOrEndIndex)
      {
        // the segment limit was a tab; going to the next tab
        currentTabHolder[0] = currentTabHolder[0] + 1;
      }
     
      if (lineMeasurer.getPosition() == paragraph.getEndIndex())
      {
        // the segment limit was the paragraph end; line completed and next line should start at normal zero x offset
        lineComplete = true;
        nextTabStopHolder[0] = null;
      }
      else
      {
        // there is paragraph text remaining
        if (lineMeasurer.getPosition() == tabIndexOrEndIndex)
        {
          // the segment limit was a tab
          if (crtSegment.rightX >= ParagraphUtil.getLastTabStop(jrParagraph, endX).getPosition())
          {
            // current segment stretches out beyond the last tab stop; line complete
            lineComplete = true;
            // next line should should start at first tab stop indent
            nextTabStopHolder[0] = ParagraphUtil.getFirstTabStop(jrParagraph, endX);
          }
          else
          {
            //nothing; this leaves lineComplete=false
          }
        }
        else
        {
          // the segment did not fit entirely
          lineComplete = true;
          if (layout == null)
          {
            // nothing fitted; next line should start at first tab stop indent
            if (nextTabStopHolder[0].getPosition() == ParagraphUtil.getFirstTabStop(jrParagraph, endX).getPosition())//FIXMETAB check based on segments.size()
            {
              // at second attempt we give up to avoid infinite loop
              nextTabStopHolder[0] = null;
              requireNextWordHolder[0] = false;
             
              //provide dummy maxFontSize because it is used for the line height of this empty line when attempting drawing below
               AttributedString tmpText =
                new AttributedString(
                  paragraph,
                  startIndex,
                  startIndex + 1
                  );
               LineBreakMeasurer lbm = new LineBreakMeasurer(tmpText.getIterator(), getFontRenderContext());
               TextLayout tlyt = lbm.nextLayout(100);
              maxAscent = tlyt.getAscent();
              maxDescent = tlyt.getDescent();
              maxLeading = tlyt.getLeading();
            }
            else
            {
              nextTabStopHolder[0] = ParagraphUtil.getFirstTabStop(jrParagraph, endX);
            }
          }
          else
          {
            // something fitted
            nextTabStopHolder[0] = null;
            requireNextWordHolder[0] = false;
          }
        }
      }

      oldSegment = crtSegment;
    }
   
    float lineHeight = AbstractTextRenderer.getLineHeight(measuredState.lines == 0, jrParagraph, maxLeading, maxAscent);
   
    if (measuredState.lines == 0) //FIXMEPARA
    //if (measuredState.paragraphStartLine == measuredState.lines)
    {
      lineHeight += jrParagraph.getSpacingBefore().intValue();
    }
   
    float newTextHeight = measuredState.textHeight + lineHeight;
    boolean fits = newTextHeight + maxDescent <= maxHeight;
    if (fits)
    {
      prevMeasuredState = measuredState.cloneState();
     
      measuredState.isLeftToRight = isLeftToRight;//run direction is per layout; but this is the best we can do for now
      measuredState.textHeight = newTextHeight;
      measuredState.lines++;

//      measuredState.fontSizeSum +=
//        maxFontSizeFinder.findMaxFontSize(
//          new AttributedString(
//            paragraph,
//            lineStartPosition,
//            lineStartPosition + characterCount
//            ).getIterator(),
//          textElement.getFontSize()
//          );

//      if (measuredState.lines == 1)
//      {
//        measuredState.firstLineLeading = measuredState.textHeight;
//        measuredState.firstLineMaxFontSize = measuredState.fontSizeSum;
//      }

      // here is the Y offset where we would draw the line
      //lastDrawPosY = drawPosY;
      //
      measuredState.textHeight += maxDescent;
     
      measuredState.textOffset += lineMeasurer.getPosition() - lineStartPosition;
     
      if (lineMeasurer.getPosition() < paragraph.getEndIndex())
      {
        //if not the last line in a paragraph, save the line break position
        measuredState.addLineBreak();
      }
//      else //FIXMEPARA
//      {
//        measuredState.textHeight += jrParagraph.getSpacingAfter().intValue();
//      }
    }
   
    return fits;
  }
 
  protected JRPropertiesHolder getTextPropertiesHolder()
  {
    return propertiesHolder;
  }
 
  protected void setAttributes(
    AttributedString string,
    AttributedCharacterIterator attributes,
    int stringOffset
    )
  {
    for (char c = attributes.first(); c != CharacterIterator.DONE; c = attributes.next())
    {
      for (Iterator it = attributes.getAttributes().entrySet().iterator(); it.hasNext();)
      {
        Map.Entry attributeEntry = (Map.Entry) it.next();
        AttributedCharacterIterator.Attribute attribute = (Attribute) attributeEntry.getKey();
        if (attributes.getRunStart(attribute) == attributes.getIndex())
        {
          Object attributeValue = attributeEntry.getValue();
          string.addAttribute(
            attribute,
            attributeValue,
            attributes.getIndex() + stringOffset,
            attributes.getRunLimit(attribute) + stringOffset
            );
        }
      }
    }
  }
 
  protected void setAttributes(
    AttributedString string,
    Map attributes,
    int startIndex,
    int endIndex
    )
  {
    for (Iterator it = attributes.entrySet().iterator(); it.hasNext();)
    {
      Map.Entry entry = (Map.Entry) it.next();
      AttributedCharacterIterator.Attribute attribute = (Attribute) entry.getKey();
      Object attributeValue = entry.getValue();
      string.addAttribute(attribute, attributeValue, startIndex, endIndex);
    }
  }

 

  /**
   *
   */
  public FontRenderContext getFontRenderContext()
  {
    return AwtTextRenderer.LINE_BREAK_FONT_RENDER_CONTEXT;
  }

 
}

class TabSegment
{
  public TextLayout layout;
  public int leftX;
  public int rightX;
}
TOP

Related Classes of net.sf.jasperreports.engine.fill.TextMeasurer$TextMeasuredState

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.