pdfbox-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From msahy...@apache.org
Subject svn commit: r1832775 - in /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout: ./ AppearanceStyle.java PlainText.java PlainTextFormatter.java
Date Sun, 03 Jun 2018 15:36:24 GMT
Author: msahyoun
Date: Sun Jun  3 15:36:24 2018
New Revision: 1832775

URL: http://svn.apache.org/viewvc?rev=1832775&view=rev
Log:
PDFBOX-4212: add text formatter classes to new ..annotation.layout package

Added:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/AppearanceStyle.java
  (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/PlainText.java
  (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/PlainTextFormatter.java
  (with props)

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/AppearanceStyle.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/AppearanceStyle.java?rev=1832775&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/AppearanceStyle.java
(added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/AppearanceStyle.java
Sun Jun  3 15:36:24 2018
@@ -0,0 +1,102 @@
+/*
+ * 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.annotation.text;
+
+import org.apache.pdfbox.pdmodel.font.PDFont;
+
+/**
+ * Define styling attributes to be used for text formatting.
+ * 
+ */
+class AppearanceStyle
+{
+    private PDFont font;
+    /**
+     * The font size to be used for text formatting.
+     *
+     * Defaulting to 12 to match Acrobats default.
+     */
+    private float fontSize = 12.0f;
+    
+    /**
+     * The leading (distance between lines) to be used for text formatting.
+     *
+     * Defaulting to 1.2*fontSize to match Acrobats default.
+     */
+    private float leading = 14.4f;
+    
+    /**
+     * Get the font used for text formatting.
+     * 
+     * @return the font used for text formatting.
+     */
+    PDFont getFont()
+    {
+        return font;
+    }
+    
+    /**
+     * Set the font to be used for text formatting.
+     * 
+     * @param font the font to be used.
+     */
+    void setFont(PDFont font)
+    {
+        this.font = font;
+    }
+    
+    /**
+     * Get the fontSize used for text formatting.
+     * 
+     * @return the fontSize used for text formatting.
+     */
+    float getFontSize()
+    {
+        return fontSize;
+    }
+    
+    /**
+     * Set the font size to be used for formatting.
+     * 
+     * @param fontSize the font size.
+     */
+    void setFontSize(float fontSize)
+    {
+        this.fontSize = fontSize;
+        leading = fontSize * 1.2f;
+    }
+
+    /**
+     * Get the leading used for text formatting.
+     * 
+     * @return the leading used for text formatting.
+     */
+    float getLeading()
+    {
+        return leading;
+    }
+    
+    /**
+     * Set the leading used for text formatting.
+     * 
+     * @param leading the leading to be used.
+     */
+    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/annotation/layout/AppearanceStyle.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/PlainText.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/PlainText.java?rev=1832775&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/PlainText.java
(added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/PlainText.java
Sun Jun  3 15:36:24 2018
@@ -0,0 +1,290 @@
+/*
+ * 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.annotation.text;
+
+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.replaceAll("\t", " ").split("\\r\\n|\\n|\\r|\\u2028|\\u2029"));
+        paragraphs = new ArrayList<>();
+        for (String part : parts)
+        {
+        	// Acrobat prints a space for an empty paragraph
+        	if (part.length() == 0)
+        	{
+        		part = " ";
+        	}
+            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<>();
+        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 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();
+            }
+            textLine.setWidth(textLine.calculateWidth(font, fontSize));
+            textLines.add(textLine);
+            return textLines;
+        }
+    }
+
+    /**
+     * An individual line of text.
+     */
+    static class Line
+    {
+        private final List<Word> words = new ArrayList<>();
+        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;
+        }
+    }
+}

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/PlainText.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/PlainTextFormatter.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/PlainTextFormatter.java?rev=1832775&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/PlainTextFormatter.java
(added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/PlainTextFormatter.java
Sun Jun  3 15:36:24 2018
@@ -0,0 +1,287 @@
+/*
+ * 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.annotation.layout;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.pdfbox.pdmodel.PDAppearanceContentStream;
+import org.apache.pdfbox.pdmodel.interactive.annotation.layout.PlainText.Line;
+import org.apache.pdfbox.pdmodel.interactive.annotation.layout.PlainText.Paragraph;
+import org.apache.pdfbox.pdmodel.interactive.annotation.layout.PlainText.TextAttribute;
+import org.apache.pdfbox.pdmodel.interactive.annotation.layout.PlainText.Word;
+
+/**
+ * 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
+{
+    
+    enum TextAlign
+    {
+        LEFT(0), CENTER(1), RIGHT(2), JUSTIFY(4);
+        
+        private final int alignment;
+        
+        private TextAlign(int alignment)
+        {
+            this.alignment = alignment;
+        }
+        
+        int getTextAlign()
+        {
+            return alignment;
+        }
+        
+        public static TextAlign valueOf(int alignment)
+        {
+            for (TextAlign textAlignment : TextAlign.values())
+            {
+                if (textAlignment.getTextAlign() == alignment)
+                {
+                    return textAlignment;
+                }
+            }
+            return TextAlign.LEFT;
+        }
+    }
+
+    /**
+     * The scaling factor for font units to PDF units
+     */
+    private static final int FONTSCALE = 1000;
+    
+    private final AppearanceStyle appearanceStyle;
+    private final boolean wrapLines;
+    private final float width;
+    
+    private final PDAppearanceContentStream contents;
+    private final PlainText textContent;
+    private final TextAlign textAlignment;
+    
+    private float horizontalOffset;
+    private float verticalOffset;
+    
+    static class Builder
+    {
+
+        // required parameters
+        private PDAppearanceContentStream contents;
+
+        // optional parameters
+        private AppearanceStyle appearanceStyle;
+        private boolean wrapLines = false;
+        private float width = 0f;
+        private PlainText textContent;
+        private TextAlign textAlignment = TextAlign.LEFT;
+        
+       
+        // initial offset from where to start the position of the first line
+        private float horizontalOffset = 0f;
+        private float verticalOffset = 0f;
+        
+        Builder(PDAppearanceContentStream contents)
+        {
+            this.contents = contents;
+        }
+
+        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 textAlign(int alignment)
+        {
+            this.textAlignment  = TextAlign.valueOf(alignment);
+            return this;
+        }
+        
+        Builder textAlign(TextAlign alignment)
+        {
+            this.textAlignment  = alignment;
+            return this;
+        }
+        
+        
+        Builder text(PlainText textContent)
+        {
+            this.textContent  = textContent;
+            return this;
+        }
+        
+        Builder initialOffset(float horizontalOffset, float verticalOffset)
+        {
+            this.horizontalOffset = horizontalOffset;
+            this.verticalOffset = verticalOffset;
+            return this;
+        }
+        
+        PlainTextFormatter build()
+        {
+            return new PlainTextFormatter(this);
+        }
+    }
+    
+    private PlainTextFormatter(Builder builder)
+    {
+        appearanceStyle = builder.appearanceStyle;
+        wrapLines = builder.wrapLines;
+        width = builder.width;
+        contents = builder.contents;
+        textContent = builder.textContent;
+        textAlignment = builder.textAlignment;
+        horizontalOffset = builder.horizontalOffset;
+        verticalOffset = builder.verticalOffset;
+    }
+    
+    /**
+     * 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())
+        {
+            boolean isFirstParagraph = true;
+        	for (Paragraph paragraph : textContent.getParagraphs())
+            {
+                if (wrapLines)
+                {
+                    List<Line> lines = paragraph.getLines(
+                                appearanceStyle.getFont(), 
+                                appearanceStyle.getFontSize(), 
+                                width
+                            );
+                    processLines(lines, isFirstParagraph);
+                    isFirstParagraph = false;
+                }
+                else
+                {
+                    float startOffset = 0f;
+                    
+                    
+                    float lineWidth = appearanceStyle.getFont().getStringWidth(paragraph.getText())
*
+                            appearanceStyle.getFontSize() / FONTSCALE;
+                    
+                    if (lineWidth < width) 
+                    {
+                        switch (textAlignment)
+                        {
+                        case CENTER:
+                            startOffset = (width - lineWidth)/2;
+                            break;
+                        case RIGHT:
+                            startOffset = width - lineWidth;
+                            break;
+                        case JUSTIFY:
+                        default:
+                            startOffset = 0f;
+                        }
+                    }
+                    
+                    contents.newLineAtOffset(horizontalOffset + startOffset, verticalOffset);
+                    contents.showText(paragraph.getText());
+                }
+            }
+        }
+    }
+
+    /**
+     * 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, boolean isFirstParagraph) throws IOException
+    {
+        float wordWidth = 0f;
+
+        float lastPos = 0f;
+        float startOffset = 0f;
+        float interWordSpacing = 0f;
+        
+        for (Line line : lines)
+        {
+            switch (textAlignment)
+            {
+            case CENTER:
+                startOffset = (width - line.getWidth())/2;
+                break;
+            case RIGHT:
+                startOffset = width - line.getWidth();
+                break;
+            case JUSTIFY:
+                if (lines.indexOf(line) != lines.size() -1)
+                {
+                    interWordSpacing = line.getInterWordSpacing(width);
+                }
+                break;
+            default:
+                startOffset = 0f;
+            }
+            
+            float offset = -lastPos + startOffset + horizontalOffset;
+            
+            if (lines.indexOf(line) == 0 && isFirstParagraph)
+            {
+                contents.newLineAtOffset(offset, verticalOffset);
+            }
+            else
+            {
+                // keep the last position
+                verticalOffset = verticalOffset - appearanceStyle.getLeading();
+                contents.newLineAtOffset(offset, - appearanceStyle.getLeading());
+            }
+
+            lastPos += offset; 
+
+            List<Word> words = line.getWords();
+            for (Word word : words)
+            {
+                contents.showText(word.getText());
+                wordWidth = (Float) word.getAttributes().getIterator().getAttribute(TextAttribute.WIDTH);
+                if (words.indexOf(word) != words.size() -1)
+                {
+                    contents.newLineAtOffset(wordWidth + interWordSpacing, 0f);
+                    lastPos = lastPos + wordWidth + interWordSpacing;
+                }
+            }
+        }
+        horizontalOffset = horizontalOffset - lastPos;
+    }
+}

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/layout/PlainTextFormatter.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message