You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ms...@apache.org on 2015/03/04 12:36:25 UTC
svn commit: r1663941 - in
/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form:
AppearanceGeneratorHelper.java AppearanceStyle.java PlainText.java
PlainTextFormatter.java
Author: msahyoun
Date: Wed Mar 4 11:36:25 2015
New Revision: 1663941
URL: http://svn.apache.org/r1663941
Log:
PDFBOX-1402, PDFBOX-2333 initial multiline handling
Added:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceStyle.java (with props)
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainText.java (with props)
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainTextFormatter.java (with props)
Modified:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java?rev=1663941&r1=1663940&r2=1663941&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java Wed Mar 4 11:36:25 2015
@@ -46,7 +46,7 @@ import org.apache.pdfbox.pdmodel.interac
import org.apache.pdfbox.contentstream.operator.Operator;
/**
- * Create the AcroForms filed appearance helper.
+ * Create the AcroForms field appearance helper.
* <p>
* A helper class to the {@link AppearanceGenerator} to generate update an AcroForm field appearance.
* </p>
@@ -325,30 +325,37 @@ class AppearanceGeneratorHelper
else
{
// Unknown quadding value - default to left
- printWriter.println(paddingLeft + " " + verticalOffset + " Td");
+ leftOffset = paddingLeft;
LOG.debug("Unknown justification value, defaulting to left: " + q);
}
- printWriter.println(leftOffset + " " + verticalOffset + " Td");
-
// show the text
- if (!isMultiLineValue(value) || stringWidth > borderEdge.getWidth() - paddingLeft -
- paddingRight)
+ if (!isMultiLine())
{
+ printWriter.println(leftOffset + " " + verticalOffset + " Td");
printWriter.flush();
COSWriter.writeString(font.encode(value), output);
printWriter.println(" Tj");
}
else
{
- String[] paragraphs = value.split("\n");
- for (int i = 0; i < paragraphs.length; i++)
- {
- boolean lastLine = i == paragraphs.length - 1;
- printWriter.flush();
- COSWriter.writeString(font.encode(value), output);
- printWriter.println(lastLine ? " Tj\n" : "> Tj 0 -13 Td");
- }
+ // adjust offset
+ // TODO: offset is dependent on border value if there is one
+ verticalOffset = verticalOffset + font.getFontDescriptor().getAscent() / 1000 * fontSize -4f;
+ printWriter.println(leftOffset + " " + verticalOffset + " Td");
+ PlainText textContent = new PlainText(value);
+ AppearanceStyle appearanceStyle = new AppearanceStyle();
+ appearanceStyle.setFont(font);
+ appearanceStyle.setFontSize(fontSize);
+ PlainTextFormatter formatter = new PlainTextFormatter
+ .Builder(output)
+ .style(appearanceStyle)
+ .text(textContent)
+ .width(borderEdge.getWidth() - paddingLeft - paddingRight)
+ .wrapLines(true)
+ .build();
+ formatter.format();
+
}
printWriter.println("ET");
printWriter.flush();
@@ -453,9 +460,9 @@ class AppearanceGeneratorHelper
}
- private boolean isMultiLineValue(String multiLineValue)
+ private boolean isMultiLine()
{
- return (parent instanceof PDTextField && ((PDTextField) parent).isMultiline() && multiLineValue.contains("\n"));
+ return (parent instanceof PDTextField && ((PDTextField) parent).isMultiline());
}
/**
Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceStyle.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceStyle.java?rev=1663941&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceStyle.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceStyle.java Wed Mar 4 11:36:25 2015
@@ -0,0 +1,72 @@
+/*
+ * 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 org.apache.pdfbox.pdmodel.interactive.form;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.pdmodel.font.PDFont;
+
+class AppearanceStyle
+{
+ private static final float FONTSCALE = 1000f;
+
+ private PDFont font;
+ private float fontSize;
+ private float leading;
+
+ PDFont getFont()
+ {
+ return font;
+ }
+
+ void setFont(PDFont font)
+ {
+ this.font = font;
+ }
+
+ float getFontSize()
+ {
+ return fontSize;
+ }
+
+ void setFontSize(float fontSize)
+ {
+ final float scale = fontSize/FONTSCALE;
+ this.fontSize = fontSize;
+ if (leading == 0)
+ {
+ try
+ {
+ leading = font.getBoundingBox().getHeight() * scale;
+ }
+ catch (IOException e)
+ {
+ leading = fontSize * 1.2f;
+ }
+ }
+ }
+
+ float getLeading()
+ {
+ return leading;
+ }
+
+ void setLeading(float leading)
+ {
+ this.leading = leading;
+ }
+}
\ No newline at end of file
Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceStyle.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceStyle.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainText.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainText.java?rev=1663941&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainText.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainText.java Wed Mar 4 11:36:25 2015
@@ -0,0 +1,262 @@
+/*
+ * 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 org.apache.pdfbox.pdmodel.interactive.form;
+
+import java.io.IOException;
+import java.text.AttributedString;
+import java.text.BreakIterator;
+import java.text.AttributedCharacterIterator.Attribute;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.pdfbox.pdmodel.font.PDFont;
+
+/**
+ * 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 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;
+ float wordWidth = 0f;
+
+ List<Line> textLines = new ArrayList<Line>();
+ Line textLine = new Line();
+
+ while (end != BreakIterator.DONE)
+ {
+ String word = textContent.substring(start,end);
+ 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(lineWidth);
+ 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 List<Word> words = new ArrayList<Word>();
+ private float lineWidth;
+
+ float getWidth()
+ {
+ return lineWidth;
+ }
+
+ void setWidth(float width)
+ {
+ lineWidth = width;
+ }
+
+ List<Word> getWords()
+ {
+ return words;
+ }
+
+ 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 String textContent;
+
+ Word(String text)
+ {
+ textContent = text;
+ }
+
+ String getText()
+ {
+ return textContent;
+ }
+
+ AttributedString getAttributes()
+ {
+ return attributedString;
+ }
+
+ void setAttributes(AttributedString as)
+ {
+ this.attributedString = as;
+ }
+ }
+}
Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainText.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainText.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainTextFormatter.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainTextFormatter.java?rev=1663941&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainTextFormatter.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainTextFormatter.java Wed Mar 4 11:36:25 2015
@@ -0,0 +1,224 @@
+/*
+ * 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 org.apache.pdfbox.pdmodel.interactive.form;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.NumberFormat;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.pdfbox.pdfwriter.COSWriter;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.interactive.form.PlainText.Line;
+import org.apache.pdfbox.pdmodel.interactive.form.PlainText.Paragraph;
+import org.apache.pdfbox.pdmodel.interactive.form.PlainText.TextAttribute;
+import org.apache.pdfbox.pdmodel.interactive.form.PlainText.Word;
+import org.apache.pdfbox.util.Charsets;
+
+/**
+ * TextFormatter to handle plain text formatting.
+ *
+ * The text formatter will take a single value or an array of values which
+ * are treated as paragraphs.
+ */
+
+class PlainTextFormatter
+{
+ private AppearanceStyle appearanceStyle;
+ private final boolean wrapLines;
+ private final float width;
+ private final OutputStream outputstream;
+ private final PlainText textContent;
+
+ // number format
+ private final NumberFormat formatDecimal = NumberFormat.getNumberInstance(Locale.US);
+
+ static class Builder
+ {
+
+ // required parameters
+ private OutputStream outputstream;
+
+ // optional parameters
+ private AppearanceStyle appearanceStyle;
+ private boolean wrapLines = false;
+ private float width = 0f;
+ private PlainText textContent;
+
+ public Builder(OutputStream outputstream)
+ {
+ this.outputstream = outputstream;
+ }
+
+ Builder style(AppearanceStyle appearanceStyle)
+ {
+ this.appearanceStyle = appearanceStyle;
+ return this;
+ }
+
+ Builder wrapLines(boolean wrapLines)
+ {
+ this.wrapLines = wrapLines;
+ return this;
+ }
+
+ Builder width(float width)
+ {
+ this.width = width;
+ return this;
+ }
+
+ Builder text(PlainText textContent)
+ {
+ this.textContent = textContent;
+ return this;
+ }
+
+ PlainTextFormatter build()
+ {
+ return new PlainTextFormatter(this);
+ }
+ }
+
+ private PlainTextFormatter(Builder builder)
+ {
+ appearanceStyle = builder.appearanceStyle;
+ wrapLines = builder.wrapLines;
+ width = builder.width;
+ outputstream = builder.outputstream;
+ textContent = builder.textContent;
+ }
+
+ /**
+ * Format the text block.
+ *
+ * @throws IOException if there is an error writing to the stream.
+ */
+ public void format() throws IOException
+ {
+ if (textContent != null && !textContent.getParagraphs().isEmpty())
+ {
+ for (Paragraph paragraph : textContent.getParagraphs())
+ {
+ if (wrapLines)
+ {
+ List<Line> lines = paragraph.getLines(
+ appearanceStyle.getFont(),
+ appearanceStyle.getFontSize(),
+ width
+ );
+ processLines(lines);
+ }
+ else
+ {
+ showText(paragraph.getText(), appearanceStyle.getFont());
+ }
+ }
+ }
+ }
+
+ /**
+ * Process lines for output.
+ *
+ * Process lines for an individual paragraph and generate the
+ * commands for the content stream to show the text.
+ *
+ * @param lines the lines to process.
+ * @throws IOException if there is an error writing to the stream.
+ */
+ private void processLines(List<Line> lines) throws IOException
+ {
+ PDFont font = appearanceStyle.getFont();
+ float wordWidth = 0f;
+
+ for (Line line : lines)
+ {
+ float offset = 0f;
+ List<Word> words = line.getWords();
+ for (Word word : words)
+ {
+ showText(word.getText(), font);
+ wordWidth = (Float) word.getAttributes().getIterator().getAttribute(TextAttribute.WIDTH);
+
+ if (words.indexOf(word) != words.size() -1)
+ {
+ newLineAtOffset(wordWidth, 0f);
+ offset = offset + wordWidth;
+ }
+ }
+ if (lines.indexOf(line) != lines.size()-1)
+ {
+ newLineAtOffset(-offset, -appearanceStyle.getLeading());
+ }
+ }
+ }
+
+ /**
+ * Shows the given text at the location specified by the current text matrix.
+ *
+ * @param text The Unicode text to show.
+ * @throws IOException if there is an error writing to the stream.
+ */
+ private void showText(String text, PDFont font) throws IOException
+ {
+ COSWriter.writeString(font.encode(text), outputstream);
+ write(" ");
+ writeOperator("Tj");
+ }
+
+ /**
+ * Writes a string to the content stream as ASCII.
+ */
+ private void write(String text) throws IOException
+ {
+ outputstream.write(text.getBytes(Charsets.US_ASCII));
+ }
+
+ /**
+ * Writes a string to the content stream as ASCII.
+ */
+ private void writeOperator(String text) throws IOException
+ {
+ outputstream.write(text.getBytes(Charsets.US_ASCII));
+ outputstream.write('\n');
+ }
+
+ /**
+ * The Td operator.
+ * Move to the start of the next line, offset from the start of the current line by (tx, ty).
+ *
+ * @param tx The x translation.
+ * @param ty The y translation.
+ * @throws IOException if there is an error writing to the stream.
+ */
+ public void newLineAtOffset(float tx, float ty) throws IOException
+ {
+ writeOperand(tx);
+ writeOperand(ty);
+ writeOperator("Td");
+ }
+
+ /**
+ * Writes a real real to the content stream.
+ */
+ private void writeOperand(float real) throws IOException
+ {
+ write(formatDecimal.format(real));
+ outputstream.write(' ');
+ }
+}
Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainTextFormatter.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PlainTextFormatter.java
------------------------------------------------------------------------------
svn:mime-type = text/plain