使用PDFBox将相同的文本重写到现有的PDF文档中

时间:2013-08-26 16:55:32

标签: java pdf pdf-generation pdfbox

这是一个非常重要的问题,我非常有兴趣得到你的帮助。

我使用PDFBox创建一个简单的PDF文档。 我试图做的是阅读现有文档,然后将相同的文本重新写入其中,并处于相同的位置。

1)首先,我创建一个名为“Musique.pdf”的PDF。

2)阅读现有文件。

3)使用PDFTextStripper将文本提取到文档中。

3)找到文档中每个字符的位置(x,y,width,fs等)。

4)创建一个必须包含每个字符的x和y的表,例如tabel1 [0] = x1 tabel1 [1] = y1,table1 [2] = x2,table1 [3] = y2等。

5)然后创建一个PDFContentStream的boucle来重写每个字符在正确的位置。

问题是:

第一行是完全写的,但问题出在第二行。

"I notice that if we have for example a text formed of 3 lines and if we assume that it contains 225 characters,,so if we get the length of this text, we will put a length equal to 231,,so we can notice that it adds 2 spaces of the end of each line,, but when we search the position of each character, the program does not consider these added spaces"

请运行我的以下代码并告诉我如何解决此问题。

我的代码到现在为止:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package test;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import org.apache.pdfbox.cos.COSInteger;
import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.pdfwriter.ContentStreamWriter;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.util.PDFOperator;
import org.apache.pdfbox.util.PDFTextStripper;
import org.apache.pdfbox.util.TextPosition;


public class Test extends PDFTextStripper{
private static final String src="...";
    private static int i;
    private static float[] table1;
    private static PDPageContentStream content;
    private static float jjj;

public Test() throws IOException {
        super.setSortByPosition(true);
    }


public static void createPdf(String src) throws IOException, COSVisitorException{


 //create  document named "Musique.pdf"

PDRectangle rec= new PDRectangle(400,400);
PDDocument document= null;
document= new PDDocument();
PDPage page= new PDPage(rec);
document.addPage(page);
PDFont font= PDType1Font.HELVETICA;
PDPageContentStream canvas1= new PDPageContentStream(document,page,true,true);
canvas1.setFont(font, 10);
canvas1.beginText();
canvas1.appendRawCommands("15 385 Td");
canvas1.appendRawCommands("(La musique est très importante dans notre vie moderne. Sans la musique, non)Tj\n");
canvas1.endText();
canvas1.close();
PDPageContentStream canvas2= new PDPageContentStream(document,page,true,true);
canvas2.setFont(font, 11);
canvas2.beginText();
canvas2.appendRawCommands("15 370 Td");
canvas2.appendRawCommands("(Donc il est très necessaire de jouer chaque jours la musique.)Tj\n");
canvas2.endText();
canvas2.close();
document.save("Musique.pdf");
document.close();

                 }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException, COSVisitorException {

Test tes= new Test();
tes.createPdf(src);

//read the existing document
PDDocument doc;
doc= PDDocument.load("Musique.pdf");
List pages = doc.getDocumentCatalog().getAllPages(); 
PDPage page = (PDPage) pages.get(0);
//extract the text existed in the document
PDFTextStripper stripper =new PDFTextStripper();
String texte=stripper.getText(doc);
PDStream contents = page.getContents();

  if(contents!=null){

      i=1;
      table1=new float[texte.length()*2]; 
      table1[0]=(float)15.0;
      //the function below call the processTextPosition procedure in order to find the position of each character and put each value in a case of table1
      tes.processStream(page, page.findResources(), page.getContents().getStream()); 

      //after execution of processTextPosition, the analysing of code continue to the below code:

 int iii=0;
int kkk=0;
//create a boucle of PDPageContentStream in order to re-write completly the text in the document
//when you run this code, you must notice a problem with the second line, so how to resolve this problem ?
PDFont font= PDType1Font.HELVETICA;
while(kkk<table1.length){
    content = new PDPageContentStream(doc,page,true,true);
    content.setFont(font, 10);
    content.beginText();
    jjj = 400-table1[kkk+1];
    content.appendRawCommands(""+table1[kkk]+" "+jjj+" Td");
    content.appendRawCommands("("+texte.charAt(iii)+")"+" Tj\n");
    content.endText();
    content.close();
    iii=iii+1;
    kkk=kkk+2;

}

  }
  //save the modified document
  doc.save("Modified-musique.pdf");
  doc.close();

}

      /**
     * @param text The text to be processed
     */

    public void processTextPosition(TextPosition text) {

        System.out.println("String[" + text.getXDirAdj() + ","
                + text.getYDirAdj() + " fs=" + text.getFontSize() + " xscale="
                + text.getXScale() + " height=" + text.getHeightDir() + " space="
                + text.getWidthOfSpace() + " width="
                + text.getWidthDirAdj() + "]" + text.getCharacter());

         if(i>1){
        table1[i]=text.getXDirAdj();
        System.out.println(table1[i]);
        i=i+1;
        table1[i]=text.getYDirAdj();
        System.out.println(table1[i]);
         i=i+1;
        }
        else{
        table1[i]=text.getYDirAdj(); 
        System.out.println(table1[i]);
        i=i+1;
         }    
    } 
}

最诚挚的问候,

李斯特。

1 个答案:

答案 0 :(得分:7)

您的概念和代码存在缺陷。

首先,这个概念:您的两个项目编号 3

  

3)使用PDFTextStripper将文本提取到文档中。

     

3)找到文档中每个字符的位置(x,y,width,fs等)。

在我眼中分离这两个步骤是一个坏主意,因为一般来说,你无法识别文本提取中的相应字符和内容中的字形。

一般来说很困难,例如内容中的e个字形对应于文本中的e个字符?计算内容流中的外观顺序与解析文本中的顺序相同只适用于非常简单的页面内容。

然后替换会带来额外的问题:例如文本提取很可能会扩展连字,例如为ff提供

此外,还有在字体编码和字符串编码之间来回切换的问题,这可能是非常有损的

此外,文本提取可能会在文本中添加空白字符,而这些字符不会出现在内容中。例如。它可以在 y 方向上识别跳跃的地方添加换行符,或者在 x 方向识别跳跃的空间中添加换行符。

顺便说一句,这很可能是你观察的原因:

  

我注意到,如果我们有一个由3行组成的文本,如果我们假设它包含225个字符,那么如果我们得到这个文本的长度,我们将把长度等于231,所以我们可以注意到它在每行的末尾添加了2个空格,但是当我们搜索每个字符的位置时,程序不会考虑这些添加的空格。

此外,您的代码会使PDF大小爆炸

  

5)然后创建一个PDFContentStream的boucle来重写每个字符在正确的位置。

while(kkk<table1.length){
    content = new PDPageContentStream(doc,page,true,true);
    ...
}

我建议至少只创建一个额外的内容流...

从这样的事情开始怎么样:

// read the existing document
PDDocument doc;
doc = PDDocument.load(musiqueFileName);
List<?> pages = doc.getDocumentCatalog().getAllPages();
PDPage page = (PDPage) pages.get(0);

PDPageContentStream content = new PDPageContentStream(doc, page, true, true);

TestRewriter rewriter = new TestRewriter(content);
rewriter.processStream(page, page.findResources(), page.getContents().getStream());

content.close();

// save the modified document
doc.save(modifiedMusiqueFileName);
doc.close();

这里TestRewriter也是PDFTextStripper的子类:

public static class TestRewriter extends PDFTextStripper
{
    final PDPageContentStream canvas;

    public TestRewriter(PDPageContentStream canvas) throws IOException
    {
        this.canvas = canvas;
    }

    /**
     * @param text
     *            The text to be processed
     */
    public void processTextPosition(TextPosition text)
    {
        try
        {
            PDFont font = PDType1Font.HELVETICA;
            canvas.setFont(font, 10);
            canvas.beginText();
            canvas.appendRawCommands("" + (text.getXDirAdj()) + " " + (400 - text.getYDirAdj()) + " Td");
            canvas.appendRawCommands("(" + text.getCharacter() + ")" + " Tj\n");
            canvas.endText();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }
}

这仍然远非完美,但可以帮助你继续......

如果您需要同时解析实际文本,请集成更多PDFTextStripper方法processTextPosition以结合功能。