/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tom_roush.pdfbox.pdmodel.interactive.form;
import com.tom_roush.pdfbox.pdmodel.font.PDFont;
import java.io.IOException;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.AttributedString;
import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A block of text.
* <p>
* A block of text can contain multiple paragraphs which will
* be treated individually within the block placement.
* </p>
*
*/
class PlainText
{
private static final float FONTSCALE = 1000f;
private final List<Paragraph> paragraphs;
/**
* Construct the text block from a single value.
*
* Constructs the text block from a single value splitting
* into individual {@link Paragraph} when a new line character is
* encountered.
*
* @param textValue the text block string.
*/
PlainText(String textValue)
{
List<String> parts = Arrays.asList(textValue.split("\\n"));
paragraphs = new ArrayList<Paragraph>();
for (String part : parts)
{
paragraphs.add(new Paragraph(part));
}
}
/**
* Construct the text block from a list of values.
*
* Constructs the text block from a list of values treating each
* entry as an individual {@link Paragraph}.
*
* @param listValue the text block string.
*/
PlainText(List<String> listValue)
{
paragraphs = new ArrayList<Paragraph>();
for (String part : listValue)
{
paragraphs.add(new Paragraph(part));
}
}
/**
* Get the list of paragraphs.
*
* @return the paragraphs.
*/
List<Paragraph> getParagraphs()
{
return paragraphs;
}
/**
* Attribute keys and attribute values used for text handling.
*
* This is similar to {@link java.awt.font.TextAttribute} but
* handled individually as to avoid a dependency on awt.
*
*/
static class TextAttribute extends Attribute
{
/**
* UID for serializing.
*/
private static final long serialVersionUID = -3138885145941283005L;
/**
* Attribute width of the text.
*/
public static final Attribute WIDTH = new TextAttribute("width");
protected TextAttribute(String name)
{
super(name);
}
}
/**
* A block of text to be formatted as a whole.
* <p>
* A block of text can contain multiple paragraphs which will
* be treated individually within the block placement.
* </p>
*
*/
static class Paragraph
{
private final String textContent;
Paragraph(String text)
{
textContent = text;
}
/**
* Get the paragraph text.
*
* @return the text.
*/
String getText()
{
return textContent;
}
/**
* Break the paragraph into individual lines.
*
* @param font the font used for rendering the text.
* @param fontSize the fontSize used for rendering the text.
* @param width the width of the box holding the content.
* @return the individual lines.
* @throws IOException
*/
List<Line> getLines(PDFont font, float fontSize, float width) throws IOException
{
BreakIterator iterator = BreakIterator.getLineInstance();
iterator.setText(textContent);
final float scale = fontSize/FONTSCALE;
int start = iterator.first();
int end = iterator.next();
float lineWidth = 0;
List<Line> textLines = new ArrayList<Line>();
Line textLine = new Line();
while (end != BreakIterator.DONE)
{
String word = textContent.substring(start,end);
float wordWidth = font.getStringWidth(word) * scale;
lineWidth = lineWidth + wordWidth;
// check if the last word would fit without the whitespace ending it
if (lineWidth >= width && Character.isWhitespace(word.charAt(word.length()-1)))
{
float whitespaceWidth =
font.getStringWidth(word.substring(word.length() - 1)) * scale;
lineWidth = lineWidth - whitespaceWidth;
}
if (lineWidth >= width)
{
textLine.setWidth(textLine.calculateWidth(font, fontSize));
textLines.add(textLine);
textLine = new Line();
lineWidth = font.getStringWidth(word) * scale;
}
AttributedString as = new AttributedString(word);
as.addAttribute(TextAttribute.WIDTH, wordWidth);
Word wordInstance = new Word(word);
wordInstance.setAttributes(as);
textLine.addWord(wordInstance);
start = end;
end = iterator.next();
}
textLines.add(textLine);
return textLines;
}
}
/**
* An individual line of text.
*/
static class Line
{
private final List<Word> words = new ArrayList<Word>();
private float lineWidth;
float getWidth()
{
return lineWidth;
}
void setWidth(float width)
{
lineWidth = width;
}
float calculateWidth(PDFont font, float fontSize) throws IOException
{
final float scale = fontSize/FONTSCALE;
float calculatedWidth = 0f;
for (Word word : words)
{
calculatedWidth = calculatedWidth +
(Float) word.getAttributes().getIterator().getAttribute(TextAttribute.WIDTH);
String text = word.getText();
if (words.indexOf(word) == words.size() -1 && Character.isWhitespace(text.charAt(text.length()-1)))
{
float whitespaceWidth = font.getStringWidth(text.substring(text.length()-1)) * scale;
calculatedWidth = calculatedWidth - whitespaceWidth;
}
}
return calculatedWidth;
}
List<Word> getWords()
{
return words;
}
float getInterWordSpacing(float width)
{
return (width - lineWidth)/(words.size()-1);
}
void addWord(Word word)
{
words.add(word);
}
}
/**
* An individual word.
*
* A word is defined as a string which must be kept
* on the same line.
*/
static class Word
{
private AttributedString attributedString;
private final String textContent;
Word(String text)
{
textContent = text;
}
String getText()
{
return textContent;
}
AttributedString getAttributes()
{
return attributedString;
}
void setAttributes(AttributedString as)
{
this.attributedString = as;
}
}
}