/* * 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; } } }