2017-08-23 1 views
2

J'essaie d'extraire du texte à partir d'un fichier PDF rempli de tableaux. Dans certains cas, une colonne est vide. Lorsque j'extrais le texte du fichier PDF, les colonnes vides sont ignorées et remplacées par un espace, par conséquent, mes expressions régulières ne peuvent pas comprendre qu'il y avait une colonne sans information à cet endroit.PDFBox: Maintien de la structure PDF lors de l'extraction de texte

image pour une meilleure compréhension:

Image of PDF source and extracted text

Nous pouvons voir que les colonnes ne sont pas respectées dans le texte extrait

Exemple de mon code qui extrait le texte du PDF:

Comment maintenir la structure complète du fichier PDF d'origine lors de l'extraction de texte à partir de celui-ci?
PDFTextStripper reader = new PDFTextStripper(); 
      reader.setSortByPosition(true); 
      reader.setStartPage(page); 
      reader.setEndPage(page); 
      String st = reader.getText(document); 
      List<String> lines = Arrays.asList(st.split(System.getProperty("line.separator"))); 

Merci beaucoup.

+0

Essayez un outil comme tabula java, qui se trouve au-dessus de PDFBox. PDFBox n'essaie pas d'identifier les tables. –

+0

Leor, si une variante du 'PDFTextStripper' vous intéresse et tente d'insérer des espaces supplémentaires dans le PDF, il y a un grand écart, je vais copier [la réponse que j'ai donnée à une question déjà supprimée] (https : //stackoverflow.com/a/28370692/1729265) avec juste une telle variante. – mkl

+0

@mkl Votre solution pourrait vous être utile. Si les espaces supplémentaires ajoutés sont toujours les mêmes (en terme de nombre de caractères), ils peuvent faire le travail. – Leor

Répondre

2

(Ce qui était à l'origine the answer (dated Feb 6 '15) to another question l'OP supprimé, y compris toutes les réponses. En raison de l'âge, le code dans la réponse était toujours basée sur PDFBox 1.8.x, de sorte que certains changements pourraient être nécessaires pour le faire fonctionner avec PDFBox 2.0 .x.)

dans les commentaires du PO ont manifesté leur intérêt dans une solution pour étendre le PDFBox PDFTextStripper retourner les lignes de texte qui tentent de refléter la mise en page du fichier PDF qui pourrait aider en cas de la question à portée de main.

Une preuve de concept car ce serait cette classe:

public class LayoutTextStripper extends PDFTextStripper 
{ 
    public LayoutTextStripper() throws IOException 
    { 
     super(); 
    } 

    @Override 
    protected void startPage(PDPage page) throws IOException 
    { 
     super.startPage(page); 
     cropBox = page.findCropBox(); 
     pageLeft = cropBox.getLowerLeftX(); 
     beginLine(); 
    } 

    @Override 
    protected void writeString(String text, List<TextPosition> textPositions) throws IOException 
    { 
     float recentEnd = 0; 
     for (TextPosition textPosition: textPositions) 
     { 
      String textHere = textPosition.getCharacter(); 
      if (textHere.trim().length() == 0) 
       continue; 

      float start = textPosition.getTextPos().getXPosition(); 
      boolean spacePresent = endsWithWS | textHere.startsWith(" "); 

      if (needsWS | spacePresent | Math.abs(start - recentEnd) > 1) 
      { 
       int spacesToInsert = insertSpaces(chars, start, needsWS & !spacePresent); 

       for (; spacesToInsert > 0; spacesToInsert--) 
       { 
        writeString(" "); 
        chars++; 
       } 
      } 

      writeString(textHere); 
      chars += textHere.length(); 

      needsWS = false; 
      endsWithWS = textHere.endsWith(" "); 
      try 
      { 
       recentEnd = getEndX(textPosition); 
      } 
      catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) 
      { 
       throw new IOException("Failure retrieving endX of TextPosition", e); 
      } 
     } 
    } 

    @Override 
    protected void writeLineSeparator() throws IOException 
    { 
     super.writeLineSeparator(); 
     beginLine(); 
    } 

    @Override 
    protected void writeWordSeparator() throws IOException 
    { 
     needsWS = true; 
    } 

    void beginLine() 
    { 
     endsWithWS = true; 
     needsWS = false; 
     chars = 0; 
    } 

    int insertSpaces(int charsInLineAlready, float chunkStart, boolean spaceRequired) 
    { 
     int indexNow = charsInLineAlready; 
     int indexToBe = (int)((chunkStart - pageLeft)/fixedCharWidth); 
     int spacesToInsert = indexToBe - indexNow; 
     if (spacesToInsert < 1 && spaceRequired) 
      spacesToInsert = 1; 

     return spacesToInsert; 
    } 

    float getEndX(TextPosition textPosition) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException 
    { 
     Field field = textPosition.getClass().getDeclaredField("endX"); 
     field.setAccessible(true); 
     return field.getFloat(textPosition); 
    } 

    public float fixedCharWidth = 3; 

    boolean endsWithWS = true; 
    boolean needsWS = false; 
    int chars = 0; 

    PDRectangle cropBox = null; 
    float pageLeft = 0; 
} 

Il est utilisé comme ceci:

PDDocument document = PDDocument.load(PDF); 

LayoutTextStripper stripper = new LayoutTextStripper(); 
stripper.setSortByPosition(true); 
stripper.fixedCharWidth = charWidth; // e.g. 5 

String text = stripper.getText(document); 

fixedCharWidth est la largeur de caractère supposé. Selon l'écriture dans le PDF en question, une valeur différente pourrait être plus à propos. Dans mon échantillon, les valeurs de 3..6 étaient intéressantes.

Il émule essentiellement la solution analogue pour iText dans this answer. Cependant, les résultats diffèrent un peu, car l'extraction de texte iText transfère des fragments de texte et l'extraction de texte PDFBox transfère des caractères individuels.

Veuillez noter qu'il ne s'agit que d'une preuve de concept. Il ne tient surtout pas compte des rotations.

+0

Votre solution fonctionne plutôt bien. Il a fallu le transformer un peu pour correspondre à la version de PDBox que j'ai utilisée, mais la première exécution est prometteuse. La structure est presque identique au fichier PDF d'origine. Je vais utiliser cette solution s'il y en a une meilleure. Merci beaucoup – Leor