2012-11-29 4 views
1

J'ai essayé de lire un flux et a été l'espoir d'obtenir pour chaque chaîne la position exacte (coordonnées)Obtenez le Stringposition exacte au format PDF

int size = reader.getXrefSize(); 

    for (int i = 0; i < size; ++i) 
    { 
     PdfObject pdfObject = reader.getPdfObject(i); 
     if ((pdfObject == null) || !pdfObject.isStream()) 
      continue; 

     PdfStream stream = (PdfStream) pdfObject; 
     PdfObject obj = stream.get(PdfName.FILTER); 

     if ((obj != null) && obj.toString().equals(PdfName.FLATEDECODE.toString())) 
     { 
      byte[] codedText = PdfReader.getStreamBytesRaw((PRStream) stream); 
      byte[] text = PdfReader.FlateDecode(codedText); 
      FileOutputStream o = new FileOutputStream(new File("/home..../Text" + i + ".txt")); 
      o.write(text); 
      o.flush(); 
      o.close(); 
     } 

    } 

En fait, j'ai obtenu la position comme

...... 
BT     
70.9 800.9 Td /F1 14 Tf <01> Tj 
10.1 0 Td <02> Tj    
9.3 0 Td <03> Tj 
3.9 0 Td <01> Tj 
10.1 0 Td <0405> Tj 
18.7 0 Td <060607> Tj 
21 0 Td <08090A07> Tj 
24.9 0 Td <05> Tj 
10.1 0 Td <0B0C0D> Tj 
28.8 0 Td <0E> Tj 
3.8 0 Td <0F> Tj 
8.6 0 Td <090B1007> Tj 
29.5 0 Td <0B11> Tj 
16.4 0 Td <12> Tj 
7.8 0 Td <1307> Tj 
12.4 0 Td <14> Tj 
7.8 0 Td <07> Tj 
3.9 0 Td <15> Tj 
7.8 0 Td <16> Tj 
7.8 0 Td <07> Tj 
3.9 0 Td <17> Tj 
10.8 0 Td <0D> Tj 
7.8 0 Td <18> Tj 
10.9 0 Td <19> Tj 
ET 
..... 

Mais Je ne sais pas quelle chaîne correspond à quelle position D'autre part dans Itext je pourrais juste obtenir le texte clair avec

PdfReader reader = new PdfReader(new FileInputStream("/home/....xxx.pdf")); 
PdfTextExtractor extract = new PdfTextExtractor(reader); 

mais bien sûr sans aucune position ....

Alors, comment puis-je obtenir la position exacte de chaque texte (chaîne, caractère, ...)?

+0

Je sais que par exemple <01> dans La ligne 1 est Ascii Hex signifiant START OF HEADING .... mais tous ne se réfèrent pas au texte réel dans le PDF – Fendrix

+1

Utilisation des classes du paquet analyseur en combinaison avec un RenderListener personnalisé. IText vous permet d'extraire du texte avec des positions et plus. Je vais écrire une réponse complète demain. – mkl

Répondre

7

Comme plinth et David van Driessche déjà souligné dans leurs réponses, extration de texte à partir du fichier PDF est non-trivial. Heureusement, les classes du paquet analyseur d'iText font la plupart des gros travaux pour vous. Vous avez déjà trouvé au moins une classe de ce package, PdfTextExtractor, mais cette classe est essentiellement un utilitaire de commodité pour utiliser la fonctionnalité d'analyseur d'iText si vous êtes uniquement intéressé par le texte brut de la page.Dans votre cas, vous devez regarder les classes dans ce paquet plus intensément.

Un point de départ pour obtenir des informations sur le sujet de l'extraction de texte avec iText est l'article 15.3 PDF Parsing de iText in Action — 2nd Edition, en particulier la méthode extractText de l'échantillon ParsingHelloWorld.java:

public void extractText(String src, String dest) throws IOException 
{ 
    PrintWriter out = new PrintWriter(new FileOutputStream(dest)); 
    PdfReader reader = new PdfReader(src); 
    RenderListener listener = new MyTextRenderListener(out); 
    PdfContentStreamProcessor processor = new PdfContentStreamProcessor(listener); 
    PdfDictionary pageDic = reader.getPageN(1); 
    PdfDictionary resourcesDic = pageDic.getAsDict(PdfName.RESOURCES); 
    processor.processContent(ContentByteUtils.getContentBytesForPage(reader, 1), resourcesDic); 
    out.flush(); 
    out.close(); 
} 

qui utilise la RenderListener la mise en œuvre MyTextRenderListener.java:

public class MyTextRenderListener implements RenderListener 
{ 
    [...] 

    /** 
    * @see RenderListener#renderText(TextRenderInfo) 
    */ 
    public void renderText(TextRenderInfo renderInfo) { 
     out.print("<"); 
     out.print(renderInfo.getText()); 
     out.print(">"); 
    } 
} 

Bien que cette RenderListener implem entation sorties simplement le texte, l'objet TextRenderInfo il inspecte offre beaucoup plus d'informations:

public LineSegment getBaseline(); // the baseline for the text (i.e. the line that the text 'sits' on) 
public LineSegment getAscentLine(); // the ascentline for the text (i.e. the line that represents the topmost extent that a string of the current font could have) 
public LineSegment getDescentLine(); // the descentline for the text (i.e. the line that represents the bottom most extent that a string of the current font could have) 
public float getRise()    ; // the rise which represents how far above the nominal baseline the text should be rendered 

public String getText();    // the text to render 
public int getTextRenderMode();  // the text render mode 
public DocumentFont getFont();  // the font 
public float getSingleSpaceWidth(); // the width, in user space units, of a single space character in the current font 

public List<TextRenderInfo> getCharacterRenderInfos(); // details useful if a listener needs access to the position of each individual glyph in the text render operation 

Ainsi, si votre RenderListener en plus d'inspecter le texte avec getText() considère également getBaseline() ou même getAscentLine() et getDescentLine(). vous avez toutes les coordonnées vous aura probablement besoin.

PS: Il y a une classe wrapper pour le code dans ParsingHelloWorld.extractText(), PdfReaderContentParser, qui vous permet d'écrire simplement ce qui suit donné un PdfReader reader, un int page, et un RenderListener renderListener:

PdfReaderContentParser parser = new PdfReaderContentParser(reader); 
parser.processContent(page, renderListener); 
+0

merci beaucoup pour votre temps à faire cela. .. :) – Fendrix

3

Si vous essayez d'extraire du texte, sachez qu'il s'agit d'un processus non trivial. Vous devrez, au minimum, implémenter une machine RPN pour exécuter le code et accumuler les transformations et exécuter tous les opérateurs de texte. Vous devrez interpréter les métriques de polices à partir de l'ensemble actuel de ressources de la page et vous devrez probablement comprendre le codage du texte. Lorsque j'ai travaillé sur Acrobat 1.0, j'étais responsable de la commande "Rechercher ..." qui incluait votre problème en tant que sous-ensemble. Avec un ensemble d'outils plus riche et plus d'expertise, il a fallu quelques mois pour bien faire les choses.

1

Si vous voulez comprendre ce que les octets que vous voyez pour l'opérateur Tj, un coup d'oeil à la spécification PDF: http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf

Plus précisément - regardez la section 9.4.3. Pour paraphraser cette section - chaque octet ou potentiellement la séquence de plusieurs octets doit être recherché dans la police utilisée pour peindre le texte (dans votre exemple la police est identifiée comme/F1). En le regardant, vous trouverez le personnage auquel ce code fait référence. Gardez également à l'esprit que l'ordre dans lequel vous voyez ces commandes de texte ici peut ne pas refléter l'ordre de lecture naturel du tout. Vous devrez donc déterminer, en fonction des positions, quel est le bon ordre de ces caractères. est. De plus, gardez à l'esprit que votre fichier PDF peut ne pas contenir d'espaces, par exemple. Puisqu'un espace peut être "truqué" en déplaçant simplement le caractère suivant un peu vers la droite, certains générateurs PDF omettent des espaces. Mais trouver un écart dans les coordonnées pourrait ne pas être une rupture de mot. Cela pourrait aussi être la fin d'une colonne par exemple.

C'est vraiment, vraiment difficile - surtout si vous essayez de le faire sur des fichiers PDF génériques (par opposition à seulement quelques mises en page que vous savez toujours provenir de la même source). J'ai écrit un éditeur de texte pour PDF il y a longtemps pour un produit appelé PitStop Pro qui est toujours là (il n'y est plus associé) et c'était vraiment un problème.

Si c'est une option, essayez d'utiliser une bibliothèque ou un outil existant. Il y a certainement des options commerciales pour une telle bibliothèque ou un tel outil; Je suis moins familier avec les bibliothèques open-source/gratuites, donc je ne peux pas commenter cela.

+0

Thx ... Je lisais en ISO_3200 - 9.6.2.2 Standard Type 1 disant codages pour les polices sont énumérés dans l'annexe D ... J'ai regardé le 10.1 0 Td <02> Tj 9.3 0 Td <03> Tj 3.9 0 Td <01> Tj. ... donc <02> signifie^B, <03> signifie^C, <01> signifie^A .... si je regarde chaque hex. dans l'exemple, aucun d'eux n'est alphabétique ... peut-être que je me méprends sur sth. ... pourriez-vous me dire exactement ce que vous entendez par En le regardant, vous trouverez le caractère auquel ce code fait référence ... – Fendrix

+1

L'erreur que vous faites est que vous regardez les encodages standard - selon toute vraisemblance votre police n'utilise pas un encodage standard. Vous devez prendre ces caractères que vous avez et les passer à travers l'encodage de la police dans votre fichier PDF (celui marqué F1 dans votre exemple). Selon le type de police, vous devrez regarder la section 9.6.6 (polices simples) ou 9.7 (polices composites) pour comprendre comment mapper ces codes dans quelque chose qui est compréhensible ... –

+0

l'a obtenu ... .thx ... la réponse était si proche ... j'inspectais toutes sortes de documents Adobe. ... en supposant que j'obtiens la bonne table CMap pour mon but ... mais finalement je me suis rendu compte que tout en balayant avec le code au-dessus de la table CMap est déjà dans un des fichiers générés ... de toute façon je marquerai la replay avec Itext répondez, mais tous les deux vous seraient accordés pour un tag de réponse ... appréciez votre aide .... – Fendrix