【问题标题】:How to generate multiple lines in PDF using Apache pdfbox如何使用 Apache pdfbox 在 PDF 中生成多行
【发布时间】:2013-11-07 06:05:10
【问题描述】:

我正在使用 Pdfbox 使用 Java 生成 PDF 文件。问题是当我在文档中添加长文本内容时,它无法正确显示。只显示其中的一部分。这也是一行。

我希望文本分多行。

我的代码如下:

PDPageContentStream pdfContent=new PDPageContentStream(pdfDocument, pdfPage, true, true);

pdfContent.beginText();
pdfContent.setFont(pdfFont, 11);
pdfContent.moveTextPositionByAmount(30,750);            
pdfContent.drawString("I am trying to create a PDF file with a lot of text contents in the document. I am using PDFBox");
pdfContent.endText();

我的输出:

【问题讨论】:

标签: java pdf-generation pdfbox


【解决方案1】:

Pdfbox-layout 抽象出管理布局的所有繁琐细节。作为一个完整的 Kotlin 示例,这里是如何将文本文件转换为 pdf 而无需担心换行和分页。

import org.apache.pdfbox.pdmodel.font.PDType1Font
import rst.pdfbox.layout.elements.Document
import rst.pdfbox.layout.elements.Paragraph
import java.io.File

fun main() {
    val textFile = "input.txt"
    val pdfFile = "output.pdf"
    val font = PDType1Font.COURIER
    val fontSize = 12f

    val document = Document(40f, 50f, 40f, 60f)
    val paragraph = Paragraph()
    File(textFile).forEachLine {
        paragraph.addText("$it\n", fontSize, font)
    }
    document.add(paragraph)
    document.save(File(pdfFile))
}

【讨论】:

    【解决方案2】:
    ///// FOR PDBOX 2.0.X
    //  FOR ADDING DYNAMIC PAGE ACCORDING THE LENGTH OF THE CONTENT
    
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.apache.pdfbox.pdmodel.PDDocument;
    import org.apache.pdfbox.pdmodel.PDPage;
    import org.apache.pdfbox.pdmodel.PDPageContentStream;
    import org.apache.pdfbox.pdmodel.common.PDRectangle;
    import org.apache.pdfbox.pdmodel.font.PDFont;
    import org.apache.pdfbox.pdmodel.font.PDType1Font;
    
    public class Document_Creation {
    
       public static void main (String args[]) throws IOException {
    
           PDDocument doc = null;
           try
           {
               doc = new PDDocument();
               PDPage page = new PDPage();
               doc.addPage(page);
               PDPageContentStream contentStream = new PDPageContentStream(doc, page);
    
               PDFont pdfFont = PDType1Font.HELVETICA;
               float fontSize = 25;
               float leading = 1.5f * fontSize;
    
               PDRectangle mediabox = page.getMediaBox();
               float margin = 72;
               float width = mediabox.getWidth() - 2*margin;
               float startX = mediabox.getLowerLeftX() + margin;
               float startY = mediabox.getUpperRightY() - margin;
    
               String text = "I am trying to create a PDF file with a lot of text contents in the document. I am using PDFBox.An essay is, generally, a piece of writing that gives the author's own argument — but the definition is vague, overlapping with those of an article, a pamphlet, and a short story. Essays have traditionally been sub-classified as formal and informal. Formal essays are characterized by serious purpose, dignity, logical organization, length,whereas the informal essay is characterized by the personal element (self-revelation, individual tastes and experiences, confidential manner), humor, graceful style, rambling structure, unconventionality or novelty of theme.Lastly, one of the most attractive features of cats as housepets is their ease of care. Cats do not have to be walked. They get plenty of exercise in the house as they play, and they do their business in the litter box. Cleaning a litter box is a quick, painless procedure. Cats also take care of their own grooming. Bathing a cat is almost never necessary because under ordinary circumstances cats clean themselves. Cats are more particular about personal cleanliness than people are. In addition, cats can be left home alone for a few hours without fear. Unlike some pets, most cats will not destroy the furnishings when left alone. They are content to go about their usual activities until their owners return."; 
               List<String> lines = new ArrayList<String>();
               int lastSpace = -1;
               while (text.length() > 0)
               {
                   int spaceIndex = text.indexOf(' ', lastSpace + 1);
                   if (spaceIndex < 0)
                       spaceIndex = text.length();
                   String subString = text.substring(0, spaceIndex);
                   float size = fontSize * pdfFont.getStringWidth(subString) / 1000;
                   System.out.printf("'%s' - %f of %f\n", subString, size, width);
                   if (size > width)
                   {
                       if (lastSpace < 0)
                           lastSpace = spaceIndex;
                       subString = text.substring(0, lastSpace);
                       lines.add(subString);
                       text = text.substring(lastSpace).trim();
                       System.out.printf("'%s' is line\n", subString);
                       lastSpace = -1;
                   }
                   else if (spaceIndex == text.length())
                   {
                       lines.add(text);
                       System.out.printf("'%s' is line\n", text);
                       text = "";
                   }
                   else
                   {
                       lastSpace = spaceIndex;
                   }
               }
    
               contentStream.beginText();
               contentStream.setFont(pdfFont, fontSize);
               contentStream.newLineAtOffset(startX, startY);
               float currentY=startY;
               for (String line: lines)
               {
                   currentY -=leading;
    
                   if(currentY<=margin)
                   {
    
                       contentStream.endText(); 
                       contentStream.close();
                       PDPage new_Page = new PDPage();
                       doc.addPage(new_Page);
                       contentStream = new PDPageContentStream(doc, new_Page);
                       contentStream.beginText();
                       contentStream.setFont(pdfFont, fontSize);
                       contentStream.newLineAtOffset(startX, startY);
                       currentY=startY;
                   }
                   contentStream.showText(line);
                   contentStream.newLineAtOffset(0, -leading);
               }
               contentStream.endText(); 
               contentStream.close();
    
               doc.save("C:/Users/VINAYAK/Desktop/docccc/break-long-string.pdf");
           }
           finally
           {
               if (doc != null)
               {
                   doc.close();
               }
           }
    
       }  
    }
    

    【讨论】:

    • 您能解释一下这如何以及为什么解决了 op 的问题吗?
    • 如果文本太长而无法在单页中设置,这只是解决早期答案的一种附加方法。
    【解决方案3】:

    除了马克的答案,您可能想知道在哪里拆分长字符串。为此,您可以使用PDFont 方法getStringWidth。

    把所有东西放在一起你会得到这样的东西(根据 PDFBox 版本略有不同):

    PDFBox 1.8.x

    PDDocument doc = null;
    try
    {
        doc = new PDDocument();
        PDPage page = new PDPage();
        doc.addPage(page);
        PDPageContentStream contentStream = new PDPageContentStream(doc, page);
    
        PDFont pdfFont = PDType1Font.HELVETICA;
        float fontSize = 25;
        float leading = 1.5f * fontSize;
    
        PDRectangle mediabox = page.findMediaBox();
        float margin = 72;
        float width = mediabox.getWidth() - 2*margin;
        float startX = mediabox.getLowerLeftX() + margin;
        float startY = mediabox.getUpperRightY() - margin;
    
        String text = "I am trying to create a PDF file with a lot of text contents in the document. I am using PDFBox"; 
        List<String> lines = new ArrayList<String>();
        int lastSpace = -1;
        while (text.length() > 0)
        {
            int spaceIndex = text.indexOf(' ', lastSpace + 1);
            if (spaceIndex < 0)
                spaceIndex = text.length();
            String subString = text.substring(0, spaceIndex);
            float size = fontSize * pdfFont.getStringWidth(subString) / 1000;
            System.out.printf("'%s' - %f of %f\n", subString, size, width);
            if (size > width)
            {
                if (lastSpace < 0)
                    lastSpace = spaceIndex;
                subString = text.substring(0, lastSpace);
                lines.add(subString);
                text = text.substring(lastSpace).trim();
                System.out.printf("'%s' is line\n", subString);
                lastSpace = -1;
            }
            else if (spaceIndex == text.length())
            {
                lines.add(text);
                System.out.printf("'%s' is line\n", text);
                text = "";
            }
            else
            {
                lastSpace = spaceIndex;
            }
        }
            
        contentStream.beginText();
        contentStream.setFont(pdfFont, fontSize);
        contentStream.moveTextPositionByAmount(startX, startY);            
        for (String line: lines)
        {
            contentStream.drawString(line);
            contentStream.moveTextPositionByAmount(0, -leading);
        }
        contentStream.endText(); 
        contentStream.close();
    
        doc.save("break-long-string.pdf");
    }
    finally
    {
        if (doc != null)
        {
            doc.close();
        }
    }
    

    (BreakLongString.java 测试 testBreakString 用于 PDFBox 1.8.x)

    PDFBox 2.0.x

    PDDocument doc = null;
    try
    {
        doc = new PDDocument();
        PDPage page = new PDPage();
        doc.addPage(page);
        PDPageContentStream contentStream = new PDPageContentStream(doc, page);
    
        PDFont pdfFont = PDType1Font.HELVETICA;
        float fontSize = 25;
        float leading = 1.5f * fontSize;
    
        PDRectangle mediabox = page.getMediaBox();
        float margin = 72;
        float width = mediabox.getWidth() - 2*margin;
        float startX = mediabox.getLowerLeftX() + margin;
        float startY = mediabox.getUpperRightY() - margin;
    
        String text = "I am trying to create a PDF file with a lot of text contents in the document. I am using PDFBox"; 
        List<String> lines = new ArrayList<String>();
        int lastSpace = -1;
        while (text.length() > 0)
        {
            int spaceIndex = text.indexOf(' ', lastSpace + 1);
            if (spaceIndex < 0)
                spaceIndex = text.length();
            String subString = text.substring(0, spaceIndex);
            float size = fontSize * pdfFont.getStringWidth(subString) / 1000;
            System.out.printf("'%s' - %f of %f\n", subString, size, width);
            if (size > width)
            {
                if (lastSpace < 0)
                    lastSpace = spaceIndex;
                subString = text.substring(0, lastSpace);
                lines.add(subString);
                text = text.substring(lastSpace).trim();
                System.out.printf("'%s' is line\n", subString);
                lastSpace = -1;
            }
            else if (spaceIndex == text.length())
            {
                lines.add(text);
                System.out.printf("'%s' is line\n", text);
                text = "";
            }
            else
            {
                lastSpace = spaceIndex;
            }
        }
    
        contentStream.beginText();
        contentStream.setFont(pdfFont, fontSize);
        contentStream.newLineAtOffset(startX, startY);
        for (String line: lines)
        {
            contentStream.showText(line);
            contentStream.newLineAtOffset(0, -leading);
        }
        contentStream.endText(); 
        contentStream.close();
    
        doc.save(new File(RESULT_FOLDER, "break-long-string.pdf"));
    }
    finally
    {
        if (doc != null)
        {
            doc.close();
        }
    }
    

    (BreakLongString.java 测试 testBreakString 用于 PDFBox 2.0.x)

    结果

    这看起来符合预期。

    当然还有很多改进要做,但这应该说明如何去做。

    添加无条件换行

    在 aleskv 的评论中问:

    当字符串中有\n时,你能添加换行符吗?

    通过首先将字符串拆分为 '\n' 字符,然后遍历拆分结果,可以轻松地将解决方案扩展为在换行符处无条件中断。

    例如if 而不是上面的长字符串

    String text = "I am trying to create a PDF file with a lot of text contents in the document. I am using PDFBox"; 
    

    您想用嵌入的换行符处理这个更长的字符串

    String textNL = "I am trying to create a PDF file with a lot of text contents in the document. I am using PDFBox.\nFurthermore, I have added some newline characters to the string at which lines also shall be broken.\nIt should work alright like this...";
    

    你可以简单地替换

    String text = "I am trying to create a PDF file with a lot of text contents in the document. I am using PDFBox"; 
    List<String> lines = new ArrayList<String>();
    int lastSpace = -1;
    while (text.length() > 0)
    {
        [...]
    }
    

    在上述解决方案中

    String textNL = "I am trying to create a PDF file with a lot of text contents in the document. I am using PDFBox.\nFurthermore, I have added some newline characters to the string at which lines also shall be broken.\nIt should work alright like this..."; 
    List<String> lines = new ArrayList<String>();
    for (String text : textNL.split("\n"))
    {
        int lastSpace = -1;
        while (text.length() > 0)
        {
            [...]
        }
    }
    

    (来自BreakLongString.java 测试testBreakStringNL)

    结果:

    【讨论】:

    • 如果您还希望您的文本是合理的,请查看this answer 在此处改进示例。
    • 感谢您的解决方案,但目前不支持单词长度超过行的最大宽度的情况。在这种情况下,这个词需要被打破,但代码没有这样做。
    • “在这种情况下,这个词需要被打破,但代码不会这样做。” - 在这种情况下,这个词是需要被打破还是需要被打破超出线边界,取决于手头的用例。由于正确的断词,即特定于语言的断字,本身就是一个主题,我在这里实现了替代解决方案。
    • 当字符串中有\n时,是否可以添加换行符?
    • @aleskv 是的,很容易。我在答案中添加了一个新部分“添加无条件换行符”。
    【解决方案4】:

    我知道这有点晚了,但我对 mkl 的解决方案有点问题。如果最后一行只包含一个单词,您的算法会将其写入前一行。

    例如:“Lorem ipsum dolor sit amet”是您的文本,它应该在“sit”之后添加一个换行符。

    Lorem ipsum dolor sit
    amet
    

    但它会这样做:

    Lorem ipsum dolor sit amet
    

    我想出了我自己的解决方案,我想与你分享。

    /**
     * @param text The text to write on the page.
     * @param x The position on the x-axis.
     * @param y The position on the y-axis.
     * @param allowedWidth The maximum allowed width of the whole text (e.g. the width of the page - a defined margin).
     * @param page The page for the text.
     * @param contentStream The content stream to set the text properties and write the text.
     * @param font The font used to write the text.
     * @param fontSize The font size used to write the text.
     * @param lineHeight The line height of the font (typically 1.2 * fontSize or 1.5 * fontSize).
     * @throws IOException
     */
    private void drawMultiLineText(String text, int x, int y, int allowedWidth, PDPage page, PDPageContentStream contentStream, PDFont font, int fontSize, int lineHeight) throws IOException {
    
        List<String> lines = new ArrayList<String>();
    
        String myLine = "";
    
        // get all words from the text
        // keep in mind that words are separated by spaces -> "Lorem ipsum!!!!:)" -> words are "Lorem" and "ipsum!!!!:)"
        String[] words = text.split(" ");
        for(String word : words) {
    
            if(!myLine.isEmpty()) {
                myLine += " ";
            }
    
            // test the width of the current line + the current word
            int size = (int) (fontSize * font.getStringWidth(myLine + word) / 1000);
            if(size > allowedWidth) {
                // if the line would be too long with the current word, add the line without the current word
                lines.add(myLine);
    
                // and start a new line with the current word
                myLine = word;
            } else {
                // if the current line + the current word would fit, add the current word to the line
                myLine += word;
            }
        }
        // add the rest to lines
        lines.add(myLine);
    
        for(String line : lines) {
            contentStream.beginText();
            contentStream.setFont(font, fontSize);
            contentStream.moveTextPositionByAmount(x, y);
            contentStream.drawString(line);
            contentStream.endText();
    
            y -= lineHeight;
        }
    
    }
    

    【讨论】:

    • 是的,正如我在回答中评论的那样,(spaceIndex &lt; 0)(即最后一句话)的情况应该以不同的方式处理。您的回答提醒我要相应地更新答案。
    【解决方案5】:

    contentStream.moveTextPositionByAmount(textx,texty) 是重点。

    例如,如果您使用的是 A4 尺寸,则意味着 580,800 是宽度和高度对应(大约)。因此您可以根据文档大小的位置移动文本。

    PDFBox 支持多种页面格式。所以不同页面格式的高度和宽度会有所不同

    【讨论】:

    • 这可能是评论而不是答案。
    【解决方案6】:

    只需在下面的位置绘制字符串,通常在循环中完成:

    float textx = margin+cellMargin;
    float texty = y-15;
    for(int i = 0; i < content.length; i++){
        for(int j = 0 ; j < content[i].length; j++){
            String text = content[i][j];
            contentStream.beginText();
            contentStream.moveTextPositionByAmount(textx,texty);
            contentStream.drawString(text);
            contentStream.endText();
            textx += colWidth;
        }
        texty-=rowHeight;
        textx = margin+cellMargin;
    }
    

    这些是重要的行:

    contentStream.beginText();
    contentStream.moveTextPositionByAmount(textx,texty);
    contentStream.drawString(text);
    contentStream.endText();
    

    只需在新位置继续绘制新字符串即可。有关使用表的示例,请参见此处: http://fahdshariff.blogspot.ca/2010/10/creating-tables-with-pdfbox.html

    【讨论】:

    • 我假设@Ronald 搜索了一种插入文本的方法,而无需手动换行,或者至少有方法支持根据字符长度进行换行。
    • @mkl 这正是我的意思。
    猜你喜欢
    • 2016-09-19
    • 2015-03-23
    • 2015-01-15
    • 1970-01-01
    • 2012-02-13
    • 2023-01-14
    • 2015-10-02
    • 2011-10-25
    相关资源
    最近更新 更多