如何使用 Apache pdfbox 在 PDF 中生成多行

问题描述 投票:0回答:7

我正在使用 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();

我的输出:

This is my output file

java pdf-generation pdfbox
7个回答
104
投票

添加到马克的答案中,您可能想知道在哪里分割长字符串。您可以使用

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.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.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)

结果

Screenshot of the result PDF displayed in Acrobat Reader

这看起来符合预期。

当然还有很多改进要做,但这应该展示如何做到这一点。

添加无条件换行符

aleskv 在评论中问道:

有的话可以加换行吗 在字符串中?

可以通过首先在 ' 处分割字符串来轻松扩展解决方案以无条件地在换行符处中断 ' 字符,然后迭代分割结果。

例如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

结果:


10
投票

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

例如:“Lorem ipsum dolor sat 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;
    }

}

7
投票
///// 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();
           }
       }

   }  
}


4
投票

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

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


2
投票

contentStream.moveTextPositionByAmount(textx,texty) 是关键点。

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

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


0
投票

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))
}

0
投票

我还想使用长字符串作为我这样的pdf文件的标题 使用 PDFBox 但出现错误...

Error message while generating PDF : U+000A ('controlLF') is not available in the font null (generic: Times-Roman), encoding: StandardEncoding


    public static void generatePDF(Invoice invoice){
String headerName = "Company name \naddress:123 main st\nphone:123456789\n" ;
            try{
                File filePath = new File("src/main/resources/generated.pdf");
                //create pdf document and page
                PDDocument pdDocument = new PDDocument();
                PDPage pdPage = new PDPage();
                //add page to the document
                pdDocument.addPage(pdPage);
                PDPageContentStream pdPageContentStream = new PDPageContentStream(pdDocument,pdPage);
    
                pdPageContentStream.beginText();
                pdPageContentStream.setFont(PDType1Font.TIMES_ROMAN,12);
    
    
                //add pdf content
    
                pdPageContentStream.newLineAtOffset(20,700);
    
                pdPageContentStream.showText(headerName);
                pdPageContentStream.newLine();
    
                pdPageContentStream.endText();
    
                pdPageContentStream.close();
    
                pdDocument.save(filePath);
                pdDocument.close();
            }catch (Exception e){
                System.err.println("Error message while generating PDF : "+ e.getMessage());
            }
        }

 

    Error message while generating PDF : U+000A ('controlLF') is not available in the font null (generic: Times-Roman), encoding: StandardEncoding
© www.soinside.com 2019 - 2024. All rights reserved.