Skip to main content
Skip table of contents

How to draw a line every 25 words?

I create a PDF using iText. The content consists of text and I need to draw a line to delimit this text every 25 words as shown in the following image:

Delimited text example

I'm aware there's a way of that I can extract text and text positions once I have a finished PDF, but considering I'm writing the text to the PDF file, I guess there could be a way of adding these lines during the creation process without really having to find the text position, right?

Posted on StackOverflow on Feb 25, 2015 by Matias Bello

Please take a look at the Every25Words example. In that example, I read a text file into a String with the readFile() method. I then split the text into words based on the occurrence of spaces, and I add each word one by one:

JAVA
protected void manipulatePdf(String dest) throws Exception {
  PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
  Document doc = new Document(pdfDoc);

  String[] words = readFile(SRC).split("\\s+");
  Paragraph paragraph = new Paragraph();
  Text text = null;
  int i = 0;
  for (String word: words) {
    if (text != null) {
      paragraph.add(" ");
    }

    text = new Text(word);
    text.setNextRenderer(new Word25TextRenderer(text, ++i));
    paragraph.add(text);
  }

  doc.add(paragraph);

  doc.close();
}

As you see we define a Word25TextRenderer to catch every 25th word and draw a dashed line. It happens through extending a TextRenderer class:

JAVA
private static class Word25TextRenderer extends TextRenderer {
  private int count = 0;

  public Word25TextRenderer(Text textElement, int count) {
    super(textElement);
    this.count = count;
  }

  // If a renderer overflows on the next area, iText uses #getNextRenderer() method to create a new renderer for the overflow part.
  // If #getNextRenderer() isn't overridden, the default method will be used and thus the default rather than the custom
  // renderer will be created
  @Override
  public IRenderer getNextRenderer() {
    return new Word25TextRenderer((Text) modelElement, count);
  }

  @Override
  public void draw(DrawContext drawContext) {
    super.draw(drawContext);

    // Draws a line to delimit the text every 25 words
    if (0 == count % 25) {
      Rectangle textRect = getOccupiedAreaBBox();
      int pageNumber = getOccupiedArea().getPageNumber();
      PdfCanvas canvas = drawContext.getCanvas();
      Rectangle pageRect = drawContext.getDocument().getPage(pageNumber).getPageSize();
      canvas
        .saveState()
        .setLineDash(5, 5)
        .moveTo(pageRect.getLeft(), textRect.getBottom())
        .lineTo(textRect.getRight(), textRect.getBottom())
        .lineTo(textRect.getRight(), textRect.getTop())
        .lineTo(pageRect.getRight(), textRect.getTop())
        .stroke()
        .restoreState();
    }
  }
}

Do you see what we do between the saveState() and restoreState() methods? We define a dash pattern, we move to the left of the page, we construct a path to the right of the word, then we draw a short upwards line, to finish the path with a line to the right. Once the path is constructed, we stroke the line. To get the area occupied by the current word we use getOccupiedAreaBBox() method and store the result to the Rectangle variable.

This draw() method will be triggered every time a Text is added on which we used the setNextRenderer() method.

This is what the result looks like every25words.pdf:

Screen shot of result

Screen shot of result

Click this link if you want to see how to answer this question in iText 5.

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.