From 459e0c8be79ce241ce0d16f24bff181dd5047ef2 Mon Sep 17 00:00:00 2001 From: yhampe Date: Tue, 7 Nov 2023 08:39:15 +0100 Subject: [PATCH 1/9] TAAS-103: --- .../internal/api/data/redaction/DocumentStructure.java | 1 + 1 file changed, 1 insertion(+) diff --git a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java index 6852896..22aaf7c 100644 --- a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java +++ b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java @@ -29,6 +29,7 @@ public class DocumentStructure { @Schema(description = "Object containing the extra field names, a table has in its properties field.") public static class TableProperties { + public static final String PAGE ="page"; public static final String NUMBER_OF_ROWS = "numberOfRows"; public static final String NUMBER_OF_COLS = "numberOfCols"; From 01493dc033e9191b985ed82862890d78a000adb3 Mon Sep 17 00:00:00 2001 From: yhampe Date: Tue, 7 Nov 2023 08:47:28 +0100 Subject: [PATCH 2/9] TAAS-103: Table Detection and rotated text * added page property to DocumentStructure to be able to get page of found tables * added a method to TableExtractionService to get the table area * added calculateMinCharWidthAndMaxCharHeightInsideTable to LayoutParsingPipeline to calculate the values based upon table area * refactored PDFLinesTextStripper for better readability *removed textMatrix from RedTextPosition as it is no longer needed --- .../processor/LayoutParsingPipeline.java | 51 +++++++++++++++++-- .../processor/model/graph/nodes/Table.java | 2 +- .../processor/model/text/RedTextPosition.java | 3 -- .../services/TableExtractionService.java | 12 ++++- .../services/mapper/PropertiesMapper.java | 5 ++ .../parsing/PDFLinesTextStripper.java | 36 ++++++++----- 6 files changed, 89 insertions(+), 20 deletions(-) diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java index 3ec8d47..5b82f93 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java @@ -26,6 +26,7 @@ import com.knecon.fforesight.service.layoutparser.processor.model.Classification import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document; import com.knecon.fforesight.service.layoutparser.processor.model.image.ClassifiedImage; import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings; +import com.knecon.fforesight.service.layoutparser.processor.model.table.Rectangle; import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock; import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence; import com.knecon.fforesight.service.layoutparser.processor.python_api.adapter.CvTableParsingAdapter; @@ -189,8 +190,15 @@ public class LayoutParsingPipeline { PDRectangle cropbox = pdPage.getCropBox(); CleanRulings cleanRulings = rulingCleaningService.getCleanRulings(pdfTableCells.get(pageNumber), stripper.getRulings(), - stripper.getMinCharWidth(), - stripper.getMaxCharHeight()); + 1, + 1); + + List spreedSheetArea = tableExtractionService.getSpreadSheetArea(cleanRulings, layoutParsingType); + + Map newValues = calculateMinCharWidthAndMaxCharHeightInsideTable(stripper,spreedSheetArea,10f,1f); + + cleanRulings = rulingCleaningService.getCleanRulings(pdfTableCells.get(pageNumber), stripper.getRulings(), newValues.get("minCharWidth"), newValues.get("maxCharHeight")); + ClassificationPage classificationPage = switch (layoutParsingType) { case REDACT_MANAGER -> redactManagerBlockificationService.blockify(stripper.getTextPositionSequences(), cleanRulings.getHorizontal(), cleanRulings.getVertical()); @@ -234,6 +242,43 @@ public class LayoutParsingPipeline { return classificationDocument; } + /** + * Finds the smallest character by width + * and the largest character by height + * inside a table area + * + * @param stripper the stripper containing the words + * @param spreedSheetArea the table area + * @param initialMinCharWidth an initial value for a minimum char width + * @param initialMaxCharHeight an initial value for a maximum char heigth + * + * @return Map with both values + */ + + private Map calculateMinCharWidthAndMaxCharHeightInsideTable(PDFLinesTextStripper stripper, List spreedSheetArea, float initialMinCharWidth, float initialMaxCharHeight) { + + float newMinCharWidth = initialMinCharWidth; + float newMaxCharHeight = initialMaxCharHeight; + Map result = new HashMap<>(); + for(var textPositionSequence: stripper.getTextPositionSequences() ) { + for(var redTextPosition: textPositionSequence.getTextPositions()) { + for(var area: spreedSheetArea) { + if(area.contains(redTextPosition.getPosition()[0], redTextPosition.getPosition()[1], redTextPosition.getPosition()[2], redTextPosition.getPosition()[3])) { + if(redTextPosition.getHeightDir() > newMaxCharHeight) { + newMaxCharHeight = redTextPosition.getHeightDir(); + } + if(redTextPosition.getWidthDirAdj() < newMinCharWidth) { + newMinCharWidth = redTextPosition.getWidthDirAdj(); + } + } + } + } + } + result.put("minCharWidth",newMinCharWidth); + result.put("maxCharHeight",newMaxCharHeight); + return result; + } + private Map> convertMarkedContents(List pdMarkedContents) { @@ -246,7 +291,7 @@ public class LayoutParsingPipeline { private void increaseDocumentStatistics(ClassificationPage classificationPage, ClassificationDocument document) { -// if (!classificationPage.isLandscape()) { + // if (!classificationPage.isLandscape()) { document.getFontSizeCounter().addAll(classificationPage.getFontSizeCounter().getCountPerValue()); // } document.getFontCounter().addAll(classificationPage.getFontCounter().getCountPerValue()); diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java index bd33f6d..b08e2b5 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java @@ -34,7 +34,7 @@ public class Table implements SemanticNode { int numberOfRows; int numberOfCols; - + int page; TextBlock textBlock; @Builder.Default diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/RedTextPosition.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/RedTextPosition.java index 92059ae..ccea113 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/RedTextPosition.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/RedTextPosition.java @@ -17,7 +17,6 @@ import lombok.SneakyThrows; @AllArgsConstructor public class RedTextPosition { - private String textMatrix; private float[] position; @JsonIgnore @@ -56,8 +55,6 @@ public class RedTextPosition { pos.setFontSizeInPt(textPosition.getFontSizeInPt()); - pos.setTextMatrix(textPosition.getTextMatrix().toString()); - var position = new float[4]; position[0] = textPosition.getXDirAdj(); diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/TableExtractionService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/TableExtractionService.java index 4fe5b10..1d486c9 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/TableExtractionService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/TableExtractionService.java @@ -79,10 +79,12 @@ public class TableExtractionService { * @param cleanRulings The lines used to build the table. * @param page Page object that contains textblocks and statistics. */ - public void extractTables(CleanRulings cleanRulings, ClassificationPage page, LayoutParsingType layoutParsingType) { + public void extractTables(CleanRulings cleanRulings, ClassificationPage page, LayoutParsingType layoutParsingType) { List cells = findCells(cleanRulings.getHorizontal(), cleanRulings.getVertical(), layoutParsingType); + + List toBeRemoved = new ArrayList<>(); for (AbstractPageBlock abstractPageBlock : page.getTextBlocks()) { @@ -134,6 +136,14 @@ public class TableExtractionService { page.getTextBlocks().removeAll(toBeRemoved); } + public List getSpreadSheetArea(CleanRulings cleanRulings, LayoutParsingType layoutParsingType) { + + List cells = findCells(cleanRulings.getHorizontal(), cleanRulings.getVertical(), layoutParsingType); + List spreadsheetAreas = findSpreadsheetsFromCells(cells).stream().filter(r -> r.getWidth() > 0f && r.getHeight() > 0f).toList(); + return spreadsheetAreas; + + } + public List findCells(List horizontalRulingLines, List verticalRulingLines, LayoutParsingType layoutParsingType) { diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java index f6c66cb..d82a257 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java @@ -1,6 +1,7 @@ package com.knecon.fforesight.service.layoutparser.processor.services.mapper; import java.awt.geom.Rectangle2D; +import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -8,6 +9,7 @@ import java.util.Map; import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructure; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Image; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.ImageType; +import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Page; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Table; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.TableCell; @@ -44,6 +46,8 @@ public class PropertiesMapper { public static Map buildTableProperties(Table table) { Map properties = new HashMap<>(); + Page page = table.getFirstPage(); + properties.put(DocumentStructure.TableProperties.PAGE, String.valueOf(page.getNumber())); properties.put(DocumentStructure.TableProperties.NUMBER_OF_ROWS, String.valueOf(table.getNumberOfRows())); properties.put(DocumentStructure.TableProperties.NUMBER_OF_COLS, String.valueOf(table.getNumberOfCols())); return properties; @@ -69,6 +73,7 @@ public class PropertiesMapper { public static void parseTableProperties(Map properties, Table.TableBuilder builder) { + builder.page(Integer.parseInt(properties.get(DocumentStructure.TableProperties.PAGE))); builder.numberOfRows(Integer.parseInt(properties.get(DocumentStructure.TableProperties.NUMBER_OF_ROWS))); builder.numberOfCols(Integer.parseInt(properties.get(DocumentStructure.TableProperties.NUMBER_OF_COLS))); } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFLinesTextStripper.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFLinesTextStripper.java index d3309bd..f92add4 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFLinesTextStripper.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFLinesTextStripper.java @@ -254,11 +254,9 @@ public class PDFLinesTextStripper extends PDFTextStripper { } // Strange but sometimes this is happening, for example: Metolachlor2.pdf - if (i > 0 && textPositions.get(i).getXDirAdj() < textPositions.get(i - 1).getXDirAdj()) { + if (checkIfCurrentPositionIsToTheRightOfPreviousPosition(i,textPositions)) { List sublist = textPositions.subList(startIndex, i); - if (!(sublist.isEmpty() || sublist.size() == 1 && (sublist.get(0).getUnicode().equals(" ") || sublist.get(0).getUnicode().equals("\u00A0") || sublist.get(0) - .getUnicode() - .equals("\t")))) { + if (checkIfSequenceContainsOnlyWhitespaces(sublist)) { textPositionSequences.add(new TextPositionSequence(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart)); } startIndex = i; @@ -266,9 +264,7 @@ public class PDFLinesTextStripper extends PDFTextStripper { if (textPositions.get(i).getRotation() == 0 && i > 0 && textPositions.get(i).getX() > textPositions.get(i - 1).getEndX() + 1) { List sublist = textPositions.subList(startIndex, i); - if (!(sublist.isEmpty() || sublist.size() == 1 && (sublist.get(0).getUnicode().equals(" ") || sublist.get(0).getUnicode().equals("\u00A0") || sublist.get(0) - .getUnicode() - .equals("\t")))) { + if (checkIfSequenceContainsOnlyWhitespaces(sublist)) { textPositionSequences.add(new TextPositionSequence(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart)); } startIndex = i; @@ -278,13 +274,10 @@ public class PDFLinesTextStripper extends PDFTextStripper { .getUnicode() .equals("\t")) && i <= textPositions.size() - 2) { List sublist = textPositions.subList(startIndex, i); - if (!(sublist.isEmpty() || sublist.size() == 1 && (sublist.get(0).getUnicode().equals(" ") || sublist.get(0).getUnicode().equals("\u00A0") || sublist.get(0) - .getUnicode() - .equals("\t")))) { + if (checkIfSequenceContainsOnlyWhitespaces(sublist)) { // Remove false sequence ends (whitespaces) - if (previous != null && sublist.get(0).getYDirAdj() == previous.getYDirAdj() && sublist.get(0) - .getXDirAdj() - (previous.getXDirAdj() + previous.getWidthDirAdj()) < 0.01) { + if (checkIfGapSizeBetweenCharactersSmallerThanMaximum(previous,sublist,0.01f)) { for (TextPosition t : sublist) { textPositionSequences.get(textPositionSequences.size() - 1).add(t); } @@ -318,6 +311,25 @@ public class PDFLinesTextStripper extends PDFTextStripper { super.writeString(text); } + public boolean checkIfCurrentPositionIsToTheRightOfPreviousPosition(int i, List textPositions) { + return i > 0 && textPositions.get(i).getXDirAdj() < textPositions.get(i - 1).getXDirAdj(); + } + + public boolean checkIfSequenceContainsOnlyWhitespaces(List sublist) { + return !(sublist.isEmpty() || sublist.size() == 1 && (sublist.get(0).getUnicode().equals(" ") || sublist.get(0).getUnicode().equals("\u00A0") || sublist.get(0) + .getUnicode() + .equals("\t"))); + } + + public boolean checkIfGapSizeBetweenCharactersSmallerThanMaximum(RedTextPosition previous, List sublist, float maximumGapSize) { + return previous != null && sublist.get(0).getYDirAdj() == previous.getYDirAdj() && sublist.get(0) + .getXDirAdj() - (previous.getXDirAdj() + previous.getWidthDirAdj()) < maximumGapSize; + } + + // !(sublist.isEmpty() || sublist.size() == 1 && (sublist.get(0).getUnicode().equals(" ") || sublist.get(0).getUnicode().equals("\u00A0") || sublist.get(0) + // .getUnicode() + // .equals("\t"))) + @Override public String getText(PDDocument doc) throws IOException { From f69331e7d838a71ce9e4f28c890b66cc863d674d Mon Sep 17 00:00:00 2001 From: yhampe Date: Tue, 7 Nov 2023 10:21:19 +0100 Subject: [PATCH 3/9] *renamed page to firstPage in DocumentStructure and Table --- .../internal/api/data/redaction/DocumentStructure.java | 2 +- .../layoutparser/processor/model/graph/nodes/Table.java | 2 +- .../processor/services/mapper/PropertiesMapper.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java index 22aaf7c..7806960 100644 --- a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java +++ b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java @@ -29,7 +29,7 @@ public class DocumentStructure { @Schema(description = "Object containing the extra field names, a table has in its properties field.") public static class TableProperties { - public static final String PAGE ="page"; + public static final String FIRST_PAGE ="firstPage"; public static final String NUMBER_OF_ROWS = "numberOfRows"; public static final String NUMBER_OF_COLS = "numberOfCols"; diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java index b08e2b5..d3c2d66 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java @@ -34,7 +34,7 @@ public class Table implements SemanticNode { int numberOfRows; int numberOfCols; - int page; + int firstpage; TextBlock textBlock; @Builder.Default diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java index d82a257..3bbe832 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java @@ -47,7 +47,7 @@ public class PropertiesMapper { Map properties = new HashMap<>(); Page page = table.getFirstPage(); - properties.put(DocumentStructure.TableProperties.PAGE, String.valueOf(page.getNumber())); + properties.put(DocumentStructure.TableProperties.FIRST_PAGE, String.valueOf(page.getNumber())); properties.put(DocumentStructure.TableProperties.NUMBER_OF_ROWS, String.valueOf(table.getNumberOfRows())); properties.put(DocumentStructure.TableProperties.NUMBER_OF_COLS, String.valueOf(table.getNumberOfCols())); return properties; @@ -73,7 +73,7 @@ public class PropertiesMapper { public static void parseTableProperties(Map properties, Table.TableBuilder builder) { - builder.page(Integer.parseInt(properties.get(DocumentStructure.TableProperties.PAGE))); + builder.firstpage(Integer.parseInt(properties.get(DocumentStructure.TableProperties.FIRST_PAGE))); builder.numberOfRows(Integer.parseInt(properties.get(DocumentStructure.TableProperties.NUMBER_OF_ROWS))); builder.numberOfCols(Integer.parseInt(properties.get(DocumentStructure.TableProperties.NUMBER_OF_COLS))); } From c3e69b2cdfc80e99d12193244a4d15041f9ceb95 Mon Sep 17 00:00:00 2001 From: yhampe Date: Wed, 15 Nov 2023 10:44:47 +0100 Subject: [PATCH 4/9] * fixed bug with incorrect empty cell count by adding threshhold to cell.contains --- .../processor/LayoutParsingPipeline.java | 20 +-- .../processor/model/graph/nodes/Table.java | 2 +- .../processor/model/table/TablePageBlock.java | 2 +- .../services/TableExtractionService.java | 33 ++++- .../server/graph/ViewerDocumentTest.java | 84 ++++++++++- .../PdfSegmentationServiceTest.java | 31 ++-- .../services/RulingCleaningServiceTest.java | 136 +++++++++++++++++- 7 files changed, 270 insertions(+), 38 deletions(-) diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java index 5b82f93..a611a52 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java @@ -190,14 +190,14 @@ public class LayoutParsingPipeline { PDRectangle cropbox = pdPage.getCropBox(); CleanRulings cleanRulings = rulingCleaningService.getCleanRulings(pdfTableCells.get(pageNumber), stripper.getRulings(), - 1, - 1); + stripper.getMinCharWidth(), + stripper.getMaxCharHeight()); List spreedSheetArea = tableExtractionService.getSpreadSheetArea(cleanRulings, layoutParsingType); - Map newValues = calculateMinCharWidthAndMaxCharHeightInsideTable(stripper,spreedSheetArea,10f,1f); + Map newValues = calculateMinCharWidthAndMaxCharHeightInsideTable(stripper,spreedSheetArea); - cleanRulings = rulingCleaningService.getCleanRulings(pdfTableCells.get(pageNumber), stripper.getRulings(), newValues.get("minCharWidth"), newValues.get("maxCharHeight")); + cleanRulings = rulingCleaningService.getCleanRulings(pdfTableCells.get(pageNumber), stripper.getRulings(), newValues.get("minCharWidth"), newValues.get("minCharHeigth")); ClassificationPage classificationPage = switch (layoutParsingType) { @@ -255,17 +255,17 @@ public class LayoutParsingPipeline { * @return Map with both values */ - private Map calculateMinCharWidthAndMaxCharHeightInsideTable(PDFLinesTextStripper stripper, List spreedSheetArea, float initialMinCharWidth, float initialMaxCharHeight) { + private Map calculateMinCharWidthAndMaxCharHeightInsideTable(PDFLinesTextStripper stripper, List spreedSheetArea) { - float newMinCharWidth = initialMinCharWidth; - float newMaxCharHeight = initialMaxCharHeight; + float newMinCharWidth = 10; + float newMinCharHeight = 30; Map result = new HashMap<>(); for(var textPositionSequence: stripper.getTextPositionSequences() ) { for(var redTextPosition: textPositionSequence.getTextPositions()) { for(var area: spreedSheetArea) { if(area.contains(redTextPosition.getPosition()[0], redTextPosition.getPosition()[1], redTextPosition.getPosition()[2], redTextPosition.getPosition()[3])) { - if(redTextPosition.getHeightDir() > newMaxCharHeight) { - newMaxCharHeight = redTextPosition.getHeightDir(); + if(redTextPosition.getHeightDir() < newMinCharHeight) { + newMinCharHeight = redTextPosition.getHeightDir(); } if(redTextPosition.getWidthDirAdj() < newMinCharWidth) { newMinCharWidth = redTextPosition.getWidthDirAdj(); @@ -275,7 +275,7 @@ public class LayoutParsingPipeline { } } result.put("minCharWidth",newMinCharWidth); - result.put("maxCharHeight",newMaxCharHeight); + result.put("minCharHeigth",newMinCharHeight); return result; } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java index d3c2d66..fb6e7b7 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java @@ -35,6 +35,7 @@ public class Table implements SemanticNode { int numberOfRows; int numberOfCols; int firstpage; + int emptyCells; TextBlock textBlock; @Builder.Default @@ -208,7 +209,6 @@ public class Table implements SemanticNode { return IntStream.range(0, numberOfCols).boxed().map(col -> getCell(row, col)); } - /** * Streams all TableCells row-wise and filters them with header == true. * diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java index 0ecf5d3..0c8a025 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java @@ -252,7 +252,7 @@ public class TablePageBlock extends AbstractPageBlock { if (prevY != null && prevX != null) { var cell = new Cell(new Point2D.Float(prevX, prevY), new Point2D.Float(x, y)); - var intersectionCell = cells.stream().filter(c -> cell.intersects(c) && cell.overlapRatio(c) > 0.1f).findFirst(); + var intersectionCell = cells.stream().filter(c -> cell.intersects(c)).findFirst(); intersectionCell.ifPresent(value -> cell.getTextBlocks().addAll(value.getTextBlocks())); if (cell.hasMinimumSize()) { row.add(cell); diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/TableExtractionService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/TableExtractionService.java index 1d486c9..284fd79 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/TableExtractionService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/TableExtractionService.java @@ -1,6 +1,9 @@ package com.knecon.fforesight.service.layoutparser.processor.services; import java.awt.geom.Point2D; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -10,6 +13,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.pdfbox.Loader; +import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType; @@ -66,6 +71,20 @@ public class TableExtractionService { }; + public boolean contains(Cell cell, double x, double y, double w, double h) { + if (cell.isEmpty() || w <= 0 || h <= 0) { + return false; + } + double x0 = cell.getX(); + double y0 = cell.getY(); + return (x >= x0-2 && + y >= y0-2 && + (x + w) <= x0 + cell.getWidth()+2 && + (y + h) <= y0 + cell.getHeight()+2); + } + + + /** * Finds tables on a page and moves textblocks into cells of the found tables. * Note: This algorithm uses Pdf Coordinate System where {0,0} rotated with the page rotation. @@ -84,13 +103,12 @@ public class TableExtractionService { List cells = findCells(cleanRulings.getHorizontal(), cleanRulings.getVertical(), layoutParsingType); - List toBeRemoved = new ArrayList<>(); for (AbstractPageBlock abstractPageBlock : page.getTextBlocks()) { TextPageBlock textBlock = (TextPageBlock) abstractPageBlock; for (Cell cell : cells) { - if (cell.hasMinimumSize() && cell.intersects(textBlock.getPdfMinX(), + if (cell.hasMinimumSize() && contains(cell, textBlock.getPdfMinX(), textBlock.getPdfMinY(), textBlock.getPdfMaxX() - textBlock.getPdfMinX(), textBlock.getPdfMaxY() - textBlock.getPdfMinY())) { @@ -104,7 +122,7 @@ public class TableExtractionService { cells = new ArrayList<>(new HashSet<>(cells)); DoubleComparisons.sort(cells, Rectangle.ILL_DEFINED_ORDER); - List spreadsheetAreas = findSpreadsheetsFromCells(cells).stream().filter(r -> r.getWidth() > 0f && r.getHeight() > 0f).toList(); + List spreadsheetAreas = findSpreadsheetsFromCells(cells); List tables = new ArrayList<>(); for (Rectangle area : spreadsheetAreas) { @@ -131,9 +149,18 @@ public class TableExtractionService { if (position != -1) { page.getTextBlocks().add(position, table); } + + String tmpFileName = "C:/Users/YANNIK~1/AppData/Local/Temp/page1.tables.html"; + try (FileOutputStream fileOutputStream = new FileOutputStream(Path.of(tmpFileName).toFile())) { + fileOutputStream.write(table.getTextAsHtml().getBytes()); + } + catch (IOException e) { + throw new RuntimeException(e); + } } page.getTextBlocks().removeAll(toBeRemoved); + } public List getSpreadSheetArea(CleanRulings cleanRulings, LayoutParsingType layoutParsingType) { diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ViewerDocumentTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ViewerDocumentTest.java index 8875e01..af0de8f 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ViewerDocumentTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ViewerDocumentTest.java @@ -2,14 +2,31 @@ package com.knecon.fforesight.service.layoutparser.server.graph; import java.io.FileOutputStream; import java.nio.file.Path; +import java.util.List; import org.apache.pdfbox.Loader; +import org.apache.pdfbox.pdmodel.PDDocument; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; +import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentData; +import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructure; +import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType; import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType; +import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationDocument; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document; +import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Table; +import com.knecon.fforesight.service.layoutparser.processor.model.table.Cell; +import com.knecon.fforesight.service.layoutparser.processor.model.table.TablePageBlock; +import com.knecon.fforesight.service.layoutparser.processor.python_api.model.image.ImageServiceResponse; +import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableServiceResponse; +import com.knecon.fforesight.service.layoutparser.processor.services.SectionsBuilderService; +import com.knecon.fforesight.service.layoutparser.processor.services.classification.RedactManagerClassificationService; +import com.knecon.fforesight.service.layoutparser.processor.services.factory.DocumentGraphFactory; +import com.knecon.fforesight.service.layoutparser.processor.services.mapper.DocumentDataMapper; +import com.knecon.fforesight.service.layoutparser.processor.services.mapper.PropertiesMapper; import com.knecon.fforesight.service.layoutparser.processor.services.visualization.LayoutGridService; import com.knecon.fforesight.service.layoutparser.processor.services.visualization.ViewerDocumentService; import com.knecon.fforesight.service.layoutparser.server.utils.BuildDocumentTest; @@ -18,19 +35,80 @@ import lombok.SneakyThrows; public class ViewerDocumentTest extends BuildDocumentTest { + @Autowired + private SectionsBuilderService sectionsBuilderService; + + @Autowired + private RedactManagerClassificationService redactManagerClassificationService; + @Test - @Disabled @SneakyThrows public void testViewerDocument() { + String fileName = "files/2Tables.pdf"; + String tmpFileName = "C:/Users/YANNIK~1/AppData/Local/Temp/2Tables.lines.pdf"; LayoutGridService layoutGridService = new LayoutGridService(); ViewerDocumentService viewerDocumentService = new ViewerDocumentService(layoutGridService); - String fileName = "files/bdr/notMergedParagraphs.pdf"; Document document = buildGraph(fileName, LayoutParsingType.TAAS); - String tmpFileName = "/tmp/" + Path.of(fileName).getFileName() + "_VIEWER.pdf"; try (var pdDocument = Loader.loadPDF(new ClassPathResource(fileName).getFile()); var out = new FileOutputStream(tmpFileName)) { viewerDocumentService.createViewerDocument(pdDocument, document, out, true); } } + @Test + @SneakyThrows + public void testTableViewerDocument() { + + String fileName = "C:\\Users\\YannikHampe\\repos\\layout-parser\\layoutparser-service\\layoutparser-service-server\\src\\test\\resources\\files\\SinglePages\\VV-931175_Page1.pdf"; + String tmpFileName = "C:/Users/YANNIK~1/AppData/Local/Temp/page1.lines.pdf"; + Document documentGraph = DocumentGraphFactory.buildDocumentGraph(layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER, + Loader.loadPDF(Path.of(fileName).toFile()), + new ImageServiceResponse(), + new TableServiceResponse())); + LayoutGridService layoutGridService = new LayoutGridService(); + ViewerDocumentService viewerDocumentService = new ViewerDocumentService(layoutGridService); + try (var pdDocument = Loader.loadPDF(Path.of(fileName).toFile()); var out = new FileOutputStream(tmpFileName)) { + viewerDocumentService.createViewerDocument(pdDocument, documentGraph, out, true); + } + //durch rows + DocumentData documentData = DocumentDataMapper.toDocumentData(documentGraph); + int emptyCellCount = 0; + List listStructure2 = documentData.getDocumentStructure() + .streamAllEntries() + .filter(entryData -> entryData.getType().equals(NodeType.TABLE)) + .map(DocumentStructure.EntryData::getProperties) + .map(properties -> { + var builder = Table.builder(); + PropertiesMapper.parseTableProperties(properties, builder); + return builder.build(); + }).toList(); + for(int i = 0; i < listStructure2.size(); i++) { + emptyCellCount = ((Table) listStructure2.get(i)).getEmptyCells(); + } + + System.out.println("Empty cells "+emptyCellCount); + + ClassificationDocument document = buildClassificationDocument(Loader.loadPDF(Path.of(fileName).toFile())); + TablePageBlock table = document.getSections().stream().flatMap(paragraph -> paragraph.getTables().stream()).toList().get(0); + int emptyCellsFoundFound = table.getRows().stream().flatMap(List::stream).toList().stream().filter(f -> f.toString().equals("")).toList().size(); + for (List row : table.getRows()) { + System.out.println(row.toString()); + } + System.out.println("Actual number of empty rows: "+emptyCellsFoundFound); + } + + public ClassificationDocument buildClassificationDocument(PDDocument originDocument) { + + ClassificationDocument classificationDocument = layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER, + originDocument, + new ImageServiceResponse(), + new TableServiceResponse()); + + redactManagerClassificationService.classifyDocument(classificationDocument); + + sectionsBuilderService.buildSections(classificationDocument); + + return classificationDocument; + } + } diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/segmentation/PdfSegmentationServiceTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/segmentation/PdfSegmentationServiceTest.java index be893a2..0ca82b5 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/segmentation/PdfSegmentationServiceTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/segmentation/PdfSegmentationServiceTest.java @@ -29,6 +29,8 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; +import javax.sound.midi.SysexMessage; + public class PdfSegmentationServiceTest extends AbstractTest { @Autowired @@ -52,7 +54,7 @@ public class PdfSegmentationServiceTest extends AbstractTest { public ClassificationDocument buildClassificationDocument(PDDocument originDocument) { - ClassificationDocument classificationDocument = layoutParsingPipeline.parseLayout(LayoutParsingType.DOCUMINE, + ClassificationDocument classificationDocument = layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER, originDocument, new ImageServiceResponse(), new TableServiceResponse()); @@ -166,8 +168,8 @@ public class PdfSegmentationServiceTest extends AbstractTest { validateTable(document, 0, 1, 1, 0, 0); validateTable(document, 1, 2, 2, 0, 0); - validateTable(document, 2, 7, 20, 0, 140); - validateTable(document, 3, 8, 31, 0, 170); + validateTable(document, 2, 7, 20, 0, 0); + validateTable(document, 3, 8, 31, 0, 0); } @@ -181,7 +183,7 @@ public class PdfSegmentationServiceTest extends AbstractTest { validateTableSize(document, 1); - validateTable(document, 0, 8, 8, 0, 2); + validateTable(document, 0, 8, 8, 0, 0); List> values = Arrays.asList(Arrays.asList("Annex point Reference within DAR/RAR", "Author, date", @@ -191,18 +193,18 @@ public class PdfSegmentationServiceTest extends AbstractTest { "Method meets analytical validation criteria", "Remarks (in case validation criteria are not met)", "Acceptability of the method"), - Arrays.asList("", + Arrays.asList("Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies", + "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies", "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies", "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies", "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies", "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies", - "", "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies", "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies"), Arrays.asList("CA 7.1.2.1.1 DAR (2009)", "Evans P.G. 2001 TMJ4569B, VV-323245", "Azoxystrobin Laboratory Degradation Study in Three Soil Types, Sampled from Holland and the United Kingdom", - "Method: RAM 269 Johnson R.I., Tummon O.J., Earl M. 1995 RJ1864B, VV-377731 Johnson R.I., Tummon O.J., Earl M. 1998 RAM 269/02, VV-124072 Johnson R.I., Tummon O.J., Earl M. 2000 RAM 269/03, VV-123986 Validation: Robinson N.J. 2001 TMJ4617B, VV-895845 in a Trial Carried", + "Method: RAM 269 Johnson R.I., Tummon O.J., Earl M. 1995 RJ1864B, VV-377731 Johnson R.I., Tummon O.J., Earl M. 1998 RAM 269/02, VV-124072 Johnson R.I., Tummon O.J., Earl M. 2000 RAM 269/03, VV-123986 Validation: Robinson N.J. 2001 TMJ4617B, VV-895845", "LC-MS/MS LOQ: 0.01 mg/kg (R401553 (SYN50165 7), R402173 (SYN501114 )) or 0.02 mg/kg (azoxystrobin, R230310, R234886) Working range: 0.02-1.0 or 0.01-0.5 mg/kg (depending on analyte) Other supporting quantificati on methods: HPLC-UV GC-MSD", "Y", "N/A", @@ -239,8 +241,8 @@ public class PdfSegmentationServiceTest extends AbstractTest { validateTableSize(document, 2); - validateTable(document, 0, 5, 5, 0, 23); - validateTable(document, 1, 11, 9, 0, 36); + validateTable(document, 0, 5, 5, 0, 0); + validateTable(document, 1, 11, 9, 0, 0); } @@ -328,7 +330,7 @@ public class PdfSegmentationServiceTest extends AbstractTest { ClassificationDocument document = buildClassificationDocument(Loader.loadPDF(pdfFileResource.getFile())); validateTableSize(document, 1); - validateTable(document, 0, 10, 6, 0, 1); + validateTable(document, 0, 10, 6, 0, 0); } @@ -450,8 +452,8 @@ public class PdfSegmentationServiceTest extends AbstractTest { validateTableSize(document, 2); - validateTable(document, 0, 6, 8, 0, 2); - validateTable(document, 1, 6, 8, 0, 1); + validateTable(document, 0, 6, 8, 0, 0); + validateTable(document, 1, 6, 8, 0, 0); } @@ -465,7 +467,7 @@ public class PdfSegmentationServiceTest extends AbstractTest { validateTableSize(document, 1); - validateTable(document, 0, 9, 5, 2, 0); + validateTable(document, 0, 9, 5, 0, 0); } @@ -490,6 +492,9 @@ public class PdfSegmentationServiceTest extends AbstractTest { List> rows = table.getRows(); int emptyCellsFoundFound = rows.stream().flatMap(List::stream).toList().stream().filter(f -> f.toString().equals("")).toList().size(); + for (List row : table.getRows()) { + row.forEach(r -> System.out.println(r.toString())); + } assertThat(emptyCellsFoundFound).isEqualTo(emptyCellsCountCorrect + emptyCellsCountIncorrect); assertThat(table.getColCount()).isEqualTo(colCount); diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java index cceec48..b1353d5 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java @@ -1,37 +1,159 @@ package com.knecon.fforesight.service.layoutparser.server.services; +import java.io.File; +import java.io.FileOutputStream; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import org.junit.jupiter.api.Test; +import javax.print.Doc; +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.junit.jupiter.api.Test; +import org.springframework.core.io.ClassPathResource; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.iqser.red.commons.jackson.ObjectMapperFactory; +import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentData; +import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructure; +import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType; +import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType; import com.knecon.fforesight.service.layoutparser.processor.model.PageContents; +import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document; +import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Table; import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings; +import com.knecon.fforesight.service.layoutparser.processor.python_api.model.image.ImageServiceResponse; +import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableServiceResponse; import com.knecon.fforesight.service.layoutparser.processor.services.PageContentExtractor; import com.knecon.fforesight.service.layoutparser.processor.services.RulingCleaningService; +import com.knecon.fforesight.service.layoutparser.processor.services.factory.DocumentGraphFactory; +import com.knecon.fforesight.service.layoutparser.processor.services.mapper.DocumentDataMapper; +import com.knecon.fforesight.service.layoutparser.processor.services.mapper.PropertiesMapper; +import com.knecon.fforesight.service.layoutparser.processor.services.parsing.PDFLinesTextStripper; +import com.knecon.fforesight.service.layoutparser.processor.services.visualization.LayoutGridService; +import com.knecon.fforesight.service.layoutparser.processor.services.visualization.ViewerDocumentService; +import com.knecon.fforesight.service.layoutparser.server.utils.BuildDocumentTest; import com.knecon.fforesight.service.layoutparser.server.utils.visualizations.PdfDraw; import lombok.SneakyThrows; -public class RulingCleaningServiceTest { +public class RulingCleaningServiceTest extends BuildDocumentTest { @Test // @Disabled @SneakyThrows public void textRulingExtraction() { - String fileName = "files/211.pdf"; - String lineFileName = "/tmp/" + Path.of(fileName).getFileName().toString() + "_LINES.pdf"; + String fileName = "/files/102 S-Metolachlor_RAR_02_Volume_2_2018-09-06.pdf"; + String lineFileName = "C:/Users/YANNIK~1/AppData/Local/Temp/102 S-Metolachlor_RAR_02_Volume_2_2018-09-06.after.pdf"; List pageContents = PageContentExtractor.getSortedPageContents(fileName); - PdfDraw.drawLinesPerPage(fileName, pageContents.stream().map(PageContents::getRulings).toList(), lineFileName); - RulingCleaningService rulingCleaningService = new RulingCleaningService(); List cleanRulingsPerPage = new LinkedList<>(); + writeJsons(Path.of(fileName)); for (PageContents pageContent : pageContents) { - cleanRulingsPerPage.add(rulingCleaningService.getCleanRulings(Collections.emptyList(), pageContent.getRulings(), 8, 20)); + cleanRulingsPerPage.add(rulingCleaningService.getCleanRulings(Collections.emptyList(), pageContent.getRulings(), 8, 1)); + } + PdfDraw.drawLinesPerPage(fileName, pageContents.stream().map(PageContents::getRulings).toList(), lineFileName); + + + } + + @Test + @SneakyThrows + public void testTableExtractionSingle() { + String filename ="C:\\Users\\YannikHampe\\repos\\layout-parser\\layoutparser-service\\layoutparser-service-server\\src\\test\\resources\\files\\SinglePages\\24 - SYN549522 - Acute Oral Toxicity - Rats_Page17.pdf"; + writeJsons(Path.of(filename)); + + } + + @Test + @SneakyThrows + public void testTableExtraction() { + + + LayoutGridService layoutGridService = new LayoutGridService(); + ViewerDocumentService viewerDocumentService = new ViewerDocumentService(layoutGridService); + + ClassPathResource resource = new ClassPathResource("files"); + List pdfFileNames = Files.walk(resource.getFile().toPath()) + .filter(path -> path.getFileName().toString().endsWith(".pdf")) + .map(Path::toAbsolutePath) + .map(Path::toString) + .toList(); + + for (int i = 0; i < pdfFileNames.size(); i++) { + writeJsons(Path.of(pdfFileNames.get(i))); } } + @SneakyThrows + private void writeJsons(Path filename) { + + Document documentGraphBefore = DocumentGraphFactory.buildDocumentGraph(layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER, + Loader.loadPDF(filename.toFile()), + new ImageServiceResponse(), + new TableServiceResponse())); + Document documentGraphAfter = DocumentGraphFactory.buildDocumentGraph(layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER, + Loader.loadPDF(filename.toFile()), + new ImageServiceResponse(), + new TableServiceResponse())); + DocumentData documentDataBefore = DocumentDataMapper.toDocumentData(documentGraphBefore); + DocumentData documentDataAfter = DocumentDataMapper.toDocumentData(documentGraphAfter); + if(!compareStructures(documentDataBefore.getDocumentStructure(), documentDataAfter.getDocumentStructure(), filename.getFileName().toString())) { + String tmpFileNameBefore = "C:/Users/YANNIK~1/AppData/Local/Temp/before."+filename.getFileName().toString();; + System.out.println(tmpFileNameBefore); + try (PDDocument pdDocument = Loader.loadPDF(filename.toFile())) { + PdfDraw.drawDocumentGraph(pdDocument, documentGraphBefore); + pdDocument.save(tmpFileNameBefore); + } + String tmpFileNameAfter = "C:/Users/YANNIK~1/AppData/Local/Temp/after."+filename.getFileName().toString();; + System.out.println(tmpFileNameAfter); + try (PDDocument pdDocument = Loader.loadPDF(filename.toFile())) { + PdfDraw.drawDocumentGraph(pdDocument, documentGraphAfter); + pdDocument.save(tmpFileNameAfter); + + } + } + } + @SneakyThrows + private boolean compareStructures(DocumentStructure structure1, DocumentStructure structure2, String pdfName) { + + + List listStructure1 = structure1 + .streamAllEntries() + .filter(entryData -> entryData.getType().equals(NodeType.TABLE)) + .map(DocumentStructure.EntryData::getProperties) + .map(properties -> { + var builder = Table.builder(); + PropertiesMapper.parseTableProperties(properties, builder); + return builder.build(); + }).toList(); + + List listStructure2 = structure2 + .streamAllEntries() + .filter(entryData -> entryData.getType().equals(NodeType.TABLE)) + .map(DocumentStructure.EntryData::getProperties) + .map(properties -> { + var builder = Table.builder(); + PropertiesMapper.parseTableProperties(properties, builder); + return builder.build(); + }).toList(); + + + for(int i = 0; i < listStructure1.size(); i++) { + Table tableNode1 = (Table) listStructure1.get(i); + Table tableNode2 = (Table) listStructure2.get(i); + if(tableNode1.getNumberOfRows() != tableNode2.getNumberOfRows() || tableNode1.getNumberOfCols() != tableNode2.getNumberOfCols()) { + return false; + } + } + return true; + } + } From a6ba66b1aabe07b5598298918a63c8eb7a501614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominique=20Eifl=C3=A4nder?= Date: Wed, 15 Nov 2023 13:36:46 +0100 Subject: [PATCH 5/9] TAAS-103: Fixed values in wrong cells --- .../processor/LayoutParsingPipeline.java | 57 +-------- .../processor/model/table/TablePageBlock.java | 20 ++- .../services/RulingCleaningService.java | 20 +-- .../services/TableExtractionService.java | 54 ++------ .../parsing/PDFLinesTextStripper.java | 83 +++++++------ .../graph/DocumentGraphVisualizationTest.java | 2 +- .../PdfSegmentationServiceTest.java | 116 ++++++++++++++---- .../services/RulingCleaningServiceTest.java | 82 ++++++------- ...A izRMS (CZ) fRR Part B9_Page185_fixed.pdf | Bin 0 -> 151195 bytes 9 files changed, 222 insertions(+), 212 deletions(-) create mode 100644 layoutparser-service/layoutparser-service-server/src/test/resources/files/SinglePages/A20622A izRMS (CZ) fRR Part B9_Page185_fixed.pdf diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java index a611a52..d628681 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java @@ -26,7 +26,6 @@ import com.knecon.fforesight.service.layoutparser.processor.model.Classification import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document; import com.knecon.fforesight.service.layoutparser.processor.model.image.ClassifiedImage; import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings; -import com.knecon.fforesight.service.layoutparser.processor.model.table.Rectangle; import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock; import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence; import com.knecon.fforesight.service.layoutparser.processor.python_api.adapter.CvTableParsingAdapter; @@ -188,17 +187,7 @@ public class LayoutParsingPipeline { boolean isLandscape = pdr.getWidth() > pdr.getHeight() && (rotation == 0 || rotation == 180) || pdr.getHeight() > pdr.getWidth() && (rotation == 90 || rotation == 270); PDRectangle cropbox = pdPage.getCropBox(); - CleanRulings cleanRulings = rulingCleaningService.getCleanRulings(pdfTableCells.get(pageNumber), - stripper.getRulings(), - stripper.getMinCharWidth(), - stripper.getMaxCharHeight()); - - List spreedSheetArea = tableExtractionService.getSpreadSheetArea(cleanRulings, layoutParsingType); - - Map newValues = calculateMinCharWidthAndMaxCharHeightInsideTable(stripper,spreedSheetArea); - - cleanRulings = rulingCleaningService.getCleanRulings(pdfTableCells.get(pageNumber), stripper.getRulings(), newValues.get("minCharWidth"), newValues.get("minCharHeigth")); - + CleanRulings cleanRulings = rulingCleaningService.getCleanRulings(pdfTableCells.get(pageNumber), stripper.getRulings()); ClassificationPage classificationPage = switch (layoutParsingType) { case REDACT_MANAGER -> redactManagerBlockificationService.blockify(stripper.getTextPositionSequences(), cleanRulings.getHorizontal(), cleanRulings.getVertical()); @@ -221,7 +210,8 @@ public class LayoutParsingPipeline { imageServiceResponseAdapter.findOcr(classificationPage); } - tableExtractionService.extractTables(cleanRulings, classificationPage, layoutParsingType); + tableExtractionService.extractTables(cleanRulings, classificationPage); + buildPageStatistics(classificationPage); increaseDocumentStatistics(classificationPage, classificationDocument); @@ -242,43 +232,6 @@ public class LayoutParsingPipeline { return classificationDocument; } - /** - * Finds the smallest character by width - * and the largest character by height - * inside a table area - * - * @param stripper the stripper containing the words - * @param spreedSheetArea the table area - * @param initialMinCharWidth an initial value for a minimum char width - * @param initialMaxCharHeight an initial value for a maximum char heigth - * - * @return Map with both values - */ - - private Map calculateMinCharWidthAndMaxCharHeightInsideTable(PDFLinesTextStripper stripper, List spreedSheetArea) { - - float newMinCharWidth = 10; - float newMinCharHeight = 30; - Map result = new HashMap<>(); - for(var textPositionSequence: stripper.getTextPositionSequences() ) { - for(var redTextPosition: textPositionSequence.getTextPositions()) { - for(var area: spreedSheetArea) { - if(area.contains(redTextPosition.getPosition()[0], redTextPosition.getPosition()[1], redTextPosition.getPosition()[2], redTextPosition.getPosition()[3])) { - if(redTextPosition.getHeightDir() < newMinCharHeight) { - newMinCharHeight = redTextPosition.getHeightDir(); - } - if(redTextPosition.getWidthDirAdj() < newMinCharWidth) { - newMinCharWidth = redTextPosition.getWidthDirAdj(); - } - } - } - } - } - result.put("minCharWidth",newMinCharWidth); - result.put("minCharHeigth",newMinCharHeight); - return result; - } - private Map> convertMarkedContents(List pdMarkedContents) { @@ -291,8 +244,8 @@ public class LayoutParsingPipeline { private void increaseDocumentStatistics(ClassificationPage classificationPage, ClassificationDocument document) { - // if (!classificationPage.isLandscape()) { - document.getFontSizeCounter().addAll(classificationPage.getFontSizeCounter().getCountPerValue()); + // if (!classificationPage.isLandscape()) { + document.getFontSizeCounter().addAll(classificationPage.getFontSizeCounter().getCountPerValue()); // } document.getFontCounter().addAll(classificationPage.getFontCounter().getCountPerValue()); document.getTextHeightCounter().addAll(classificationPage.getTextHeightCounter().getCountPerValue()); diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java index 0c8a025..45b756c 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java @@ -1,12 +1,14 @@ package com.knecon.fforesight.service.layoutparser.processor.model.table; import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeMap; +import java.util.stream.Collectors; import com.knecon.fforesight.service.layoutparser.processor.model.AbstractPageBlock; import com.knecon.fforesight.service.layoutparser.processor.model.PageBlockType; @@ -252,7 +254,8 @@ public class TablePageBlock extends AbstractPageBlock { if (prevY != null && prevX != null) { var cell = new Cell(new Point2D.Float(prevX, prevY), new Point2D.Float(x, y)); - var intersectionCell = cells.stream().filter(c -> cell.intersects(c)).findFirst(); + var intersectionCell = cells.stream().filter(c -> intersects(cell, c)).findFirst(); + intersectionCell.ifPresent(value -> cell.getTextBlocks().addAll(value.getTextBlocks())); if (cell.hasMinimumSize()) { row.add(cell); @@ -273,6 +276,21 @@ public class TablePageBlock extends AbstractPageBlock { } + + public boolean intersects(Cell cell1, Cell cell2) { + if (cell1.getHeight() <= 0 || cell1.getHeight() <= 0 || cell2.getHeight() <= 0 || cell2.getHeight() <= 0) { + return false; + } + double x0 = cell1.getX() + 2; + double y0 = cell1.getY() + 2; + return (cell2.x + cell2.width > x0 && + cell2.y + cell2.height > y0 && + cell2.x < x0 + cell1.getWidth() -2 && + cell2.y < y0 + cell1.getHeight() -2); + } + + + @Override public String getText() { diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/RulingCleaningService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/RulingCleaningService.java index f18cfee..b24157c 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/RulingCleaningService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/RulingCleaningService.java @@ -12,9 +12,9 @@ import java.util.Map; import org.springframework.stereotype.Service; -import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableCells; import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings; import com.knecon.fforesight.service.layoutparser.processor.model.table.Ruling; +import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableCells; import com.knecon.fforesight.service.layoutparser.processor.utils.DoubleComparisons; import lombok.RequiredArgsConstructor; @@ -25,10 +25,13 @@ import lombok.extern.slf4j.Slf4j; @RequiredArgsConstructor public class RulingCleaningService { - public CleanRulings getCleanRulings(List tableCells, List rulings, float minCharWidth, float maxCharHeight) { + private static final float THRESHOLD = 6; + + + public CleanRulings getCleanRulings(List tableCells, List rulings) { if (!rulings.isEmpty()) { - snapPoints(rulings, minCharWidth, maxCharHeight); + snapPoints(rulings); } List vrs = new ArrayList<>(); @@ -53,14 +56,11 @@ public class RulingCleaningService { } List horizontalRulingLines = collapseOrientedRulings(hrs); - return CleanRulings.builder() - .vertical(verticalRulingLines) - .horizontal(horizontalRulingLines) - .build(); + return CleanRulings.builder().vertical(verticalRulingLines).horizontal(horizontalRulingLines).build(); } - public void snapPoints(List rulings, float xThreshold, float yThreshold) { + public void snapPoints(List rulings) { // collect points and keep a Line -> p1,p2 map Map linesToPoints = new HashMap<>(); @@ -81,7 +81,7 @@ public class RulingCleaningService { for (Point2D p : points.subList(1, points.size() - 1)) { List last = groupedPoints.get(groupedPoints.size() - 1); - if (Math.abs(p.getX() - last.get(0).getX()) < xThreshold) { + if (Math.abs(p.getX() - last.get(0).getX()) < THRESHOLD) { groupedPoints.get(groupedPoints.size() - 1).add(p); } else { groupedPoints.add(new ArrayList<>(Collections.singletonList(p))); @@ -108,7 +108,7 @@ public class RulingCleaningService { for (Point2D p : points.subList(1, points.size() - 1)) { List last = groupedPoints.get(groupedPoints.size() - 1); - if (Math.abs(p.getY() - last.get(0).getY()) < yThreshold) { + if (Math.abs(p.getY() - last.get(0).getY()) < THRESHOLD) { groupedPoints.get(groupedPoints.size() - 1).add(p); } else { groupedPoints.add(new ArrayList<>(Collections.singletonList(p))); diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/TableExtractionService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/TableExtractionService.java index 284fd79..dd6bcc8 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/TableExtractionService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/TableExtractionService.java @@ -1,9 +1,6 @@ package com.knecon.fforesight.service.layoutparser.processor.services; import java.awt.geom.Point2D; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -13,11 +10,8 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.pdfbox.Loader; -import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; -import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType; import com.knecon.fforesight.service.layoutparser.processor.model.AbstractPageBlock; import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationPage; import com.knecon.fforesight.service.layoutparser.processor.model.table.Cell; @@ -72,19 +66,16 @@ public class TableExtractionService { public boolean contains(Cell cell, double x, double y, double w, double h) { + if (cell.isEmpty() || w <= 0 || h <= 0) { return false; } double x0 = cell.getX(); double y0 = cell.getY(); - return (x >= x0-2 && - y >= y0-2 && - (x + w) <= x0 + cell.getWidth()+2 && - (y + h) <= y0 + cell.getHeight()+2); + return (x >= x0 - 2 && y >= y0 - 2 && (x + w) <= x0 + cell.getWidth() + 2 && (y + h) <= y0 + cell.getHeight() + 2); } - /** * Finds tables on a page and moves textblocks into cells of the found tables. * Note: This algorithm uses Pdf Coordinate System where {0,0} rotated with the page rotation. @@ -98,17 +89,17 @@ public class TableExtractionService { * @param cleanRulings The lines used to build the table. * @param page Page object that contains textblocks and statistics. */ - public void extractTables(CleanRulings cleanRulings, ClassificationPage page, LayoutParsingType layoutParsingType) { - - List cells = findCells(cleanRulings.getHorizontal(), cleanRulings.getVertical(), layoutParsingType); + public void extractTables(CleanRulings cleanRulings, ClassificationPage page) { + List cells = findCells(cleanRulings.getHorizontal(), cleanRulings.getVertical()); List toBeRemoved = new ArrayList<>(); for (AbstractPageBlock abstractPageBlock : page.getTextBlocks()) { TextPageBlock textBlock = (TextPageBlock) abstractPageBlock; for (Cell cell : cells) { - if (cell.hasMinimumSize() && contains(cell, textBlock.getPdfMinX(), + if (cell.hasMinimumSize() && contains(cell, + textBlock.getPdfMinX(), textBlock.getPdfMinY(), textBlock.getPdfMaxX() - textBlock.getPdfMinX(), textBlock.getPdfMaxY() - textBlock.getPdfMinY())) { @@ -149,39 +140,20 @@ public class TableExtractionService { if (position != -1) { page.getTextBlocks().add(position, table); } - - String tmpFileName = "C:/Users/YANNIK~1/AppData/Local/Temp/page1.tables.html"; - try (FileOutputStream fileOutputStream = new FileOutputStream(Path.of(tmpFileName).toFile())) { - fileOutputStream.write(table.getTextAsHtml().getBytes()); - } - catch (IOException e) { - throw new RuntimeException(e); - } } page.getTextBlocks().removeAll(toBeRemoved); - - } - - public List getSpreadSheetArea(CleanRulings cleanRulings, LayoutParsingType layoutParsingType) { - - List cells = findCells(cleanRulings.getHorizontal(), cleanRulings.getVertical(), layoutParsingType); - List spreadsheetAreas = findSpreadsheetsFromCells(cells).stream().filter(r -> r.getWidth() > 0f && r.getHeight() > 0f).toList(); - return spreadsheetAreas; - } - public List findCells(List horizontalRulingLines, List verticalRulingLines, LayoutParsingType layoutParsingType) { + public List findCells(List horizontalRulingLines, List verticalRulingLines) { - if (layoutParsingType.equals(LayoutParsingType.TAAS)) { - // TODO: breaks some tables, for example "1 Abamectin Prr.pdf" try to fix this upstream in RulingCleaningService - for (Ruling r : horizontalRulingLines) { - if (r.getX2() < r.getX1()) { - double a = r.getX2(); - r.x2 = (float) r.getX1(); - r.x1 = (float) a; - } + // Fix for 211.pdf + for (Ruling r : horizontalRulingLines) { + if (r.getX2() < r.getX1()) { + double a = r.getX2(); + r.x2 = (float) r.getX1(); + r.x1 = (float) a; } } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFLinesTextStripper.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFLinesTextStripper.java index f92add4..f7b523f 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFLinesTextStripper.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFLinesTextStripper.java @@ -1,18 +1,34 @@ package com.knecon.fforesight.service.layoutparser.processor.services.parsing; -import com.knecon.fforesight.service.layoutparser.processor.model.table.Ruling; -import com.knecon.fforesight.service.layoutparser.processor.model.text.RedTextPosition; -import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence; -import lombok.Getter; -import lombok.Setter; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; +import java.awt.color.CMMException; +import java.awt.geom.Point2D; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + import org.apache.pdfbox.contentstream.operator.Operator; import org.apache.pdfbox.contentstream.operator.OperatorName; -import org.apache.pdfbox.contentstream.operator.color.*; +import org.apache.pdfbox.contentstream.operator.color.SetNonStrokingColor; +import org.apache.pdfbox.contentstream.operator.color.SetNonStrokingColorN; +import org.apache.pdfbox.contentstream.operator.color.SetNonStrokingColorSpace; +import org.apache.pdfbox.contentstream.operator.color.SetNonStrokingDeviceCMYKColor; +import org.apache.pdfbox.contentstream.operator.color.SetNonStrokingDeviceGrayColor; +import org.apache.pdfbox.contentstream.operator.color.SetNonStrokingDeviceRGBColor; +import org.apache.pdfbox.contentstream.operator.color.SetStrokingColor; +import org.apache.pdfbox.contentstream.operator.color.SetStrokingColorN; +import org.apache.pdfbox.contentstream.operator.color.SetStrokingColorSpace; +import org.apache.pdfbox.contentstream.operator.color.SetStrokingDeviceCMYKColor; +import org.apache.pdfbox.contentstream.operator.color.SetStrokingDeviceGrayColor; +import org.apache.pdfbox.contentstream.operator.color.SetStrokingDeviceRGBColor; import org.apache.pdfbox.contentstream.operator.markedcontent.BeginMarkedContentSequenceWithProperties; import org.apache.pdfbox.contentstream.operator.markedcontent.EndMarkedContentSequence; -import org.apache.pdfbox.contentstream.operator.state.*; +import org.apache.pdfbox.contentstream.operator.state.SetFlatness; +import org.apache.pdfbox.contentstream.operator.state.SetLineCapStyle; +import org.apache.pdfbox.contentstream.operator.state.SetLineDashPattern; +import org.apache.pdfbox.contentstream.operator.state.SetLineJoinStyle; +import org.apache.pdfbox.contentstream.operator.state.SetLineMiterLimit; +import org.apache.pdfbox.contentstream.operator.state.SetLineWidth; +import org.apache.pdfbox.contentstream.operator.state.SetRenderingIntent; import org.apache.pdfbox.contentstream.operator.text.SetFontAndSize; import org.apache.pdfbox.cos.COSBase; import org.apache.pdfbox.cos.COSNumber; @@ -21,11 +37,14 @@ import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.graphics.color.PDColor; import org.apache.pdfbox.text.TextPosition; -import java.awt.color.CMMException; -import java.awt.geom.Point2D; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import com.knecon.fforesight.service.layoutparser.processor.model.table.Ruling; +import com.knecon.fforesight.service.layoutparser.processor.model.text.RedTextPosition; +import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence; + +import lombok.Getter; +import lombok.Setter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; @Getter @Slf4j @@ -36,11 +55,6 @@ public class PDFLinesTextStripper extends PDFTextStripper { private final List graphicsPath = new ArrayList<>(); @Setter protected PDPage pdpage; - private int minCharWidth; - private int maxCharWidth; - private int minCharHeight; - private int maxCharHeight; - private float path_x; private float path_y; @@ -73,7 +87,6 @@ public class PDFLinesTextStripper extends PDFTextStripper { this.addOperator(new SetFontAndSize(this)); this.addOperator(new SetLineWidth(this)); - addOperator(new BeginMarkedContentSequenceWithProperties(this)); // addOperator(new BeginMarkedContentSequence(this)); addOperator(new EndMarkedContentSequence(this)); @@ -232,29 +245,13 @@ public class PDFLinesTextStripper extends PDFTextStripper { .get(textPositionSequences.get(textPositionSequences.size() - 1).getTextPositions().size() - 1); } - int charWidth = (int) textPositions.get(i).getWidthDirAdj(); - if (charWidth < minCharWidth) { - minCharWidth = charWidth; - } - if (charWidth > maxCharWidth) { - maxCharWidth = charWidth; - } - - int charHeight = (int) textPositions.get(i).getHeightDir(); - if (charHeight < minCharHeight) { - minCharHeight = charHeight; - } - if (charWidth > maxCharHeight) { - maxCharHeight = charHeight; - } - if (i == 0 && (textPositions.get(i).getUnicode().equals(" ") || textPositions.get(i).getUnicode().equals("\u00A0") || textPositions.get(i).getUnicode().equals("\t"))) { startIndex++; continue; } // Strange but sometimes this is happening, for example: Metolachlor2.pdf - if (checkIfCurrentPositionIsToTheRightOfPreviousPosition(i,textPositions)) { + if (checkIfCurrentPositionIsToTheRightOfPreviousPosition(i, textPositions)) { List sublist = textPositions.subList(startIndex, i); if (checkIfSequenceContainsOnlyWhitespaces(sublist)) { textPositionSequences.add(new TextPositionSequence(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart)); @@ -277,7 +274,7 @@ public class PDFLinesTextStripper extends PDFTextStripper { if (checkIfSequenceContainsOnlyWhitespaces(sublist)) { // Remove false sequence ends (whitespaces) - if (checkIfGapSizeBetweenCharactersSmallerThanMaximum(previous,sublist,0.01f)) { + if (checkIfGapSizeBetweenCharactersSmallerThanMaximum(previous, sublist, 0.01f)) { for (TextPosition t : sublist) { textPositionSequences.get(textPositionSequences.size() - 1).add(t); } @@ -311,17 +308,23 @@ public class PDFLinesTextStripper extends PDFTextStripper { super.writeString(text); } + public boolean checkIfCurrentPositionIsToTheRightOfPreviousPosition(int i, List textPositions) { + return i > 0 && textPositions.get(i).getXDirAdj() < textPositions.get(i - 1).getXDirAdj(); } + public boolean checkIfSequenceContainsOnlyWhitespaces(List sublist) { + return !(sublist.isEmpty() || sublist.size() == 1 && (sublist.get(0).getUnicode().equals(" ") || sublist.get(0).getUnicode().equals("\u00A0") || sublist.get(0) .getUnicode() .equals("\t"))); } + public boolean checkIfGapSizeBetweenCharactersSmallerThanMaximum(RedTextPosition previous, List sublist, float maximumGapSize) { + return previous != null && sublist.get(0).getYDirAdj() == previous.getYDirAdj() && sublist.get(0) .getXDirAdj() - (previous.getXDirAdj() + previous.getWidthDirAdj()) < maximumGapSize; } @@ -334,10 +337,6 @@ public class PDFLinesTextStripper extends PDFTextStripper { @Override public String getText(PDDocument doc) throws IOException { - minCharWidth = Integer.MAX_VALUE; - maxCharWidth = 0; - minCharHeight = Integer.MAX_VALUE; - maxCharHeight = 0; textPositionSequences.clear(); rulings.clear(); graphicsPath.clear(); diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/DocumentGraphVisualizationTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/DocumentGraphVisualizationTest.java index 2fd6aae..5f150e2 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/DocumentGraphVisualizationTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/DocumentGraphVisualizationTest.java @@ -47,7 +47,7 @@ public class DocumentGraphVisualizationTest extends BuildDocumentTest { @Disabled public void visualizeCraftedDocument() { - String filename = "files/crafted document.pdf"; + String filename = "files/1 Abamectin_prr.pdf"; visualizePdf(filename); } diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/segmentation/PdfSegmentationServiceTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/segmentation/PdfSegmentationServiceTest.java index 0ca82b5..a35b401 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/segmentation/PdfSegmentationServiceTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/segmentation/PdfSegmentationServiceTest.java @@ -1,5 +1,27 @@ package com.knecon.fforesight.service.layoutparser.server.segmentation; +import static org.assertj.core.api.Assertions.assertThat; + +import java.awt.geom.Rectangle2D; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; + import com.fasterxml.jackson.databind.ObjectMapper; import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType; import com.knecon.fforesight.service.layoutparser.processor.LayoutParsingPipeline; @@ -15,21 +37,8 @@ import com.knecon.fforesight.service.layoutparser.processor.python_api.model.tab import com.knecon.fforesight.service.layoutparser.processor.services.SectionsBuilderService; import com.knecon.fforesight.service.layoutparser.processor.services.classification.RedactManagerClassificationService; import com.knecon.fforesight.service.layoutparser.server.utils.AbstractTest; + import lombok.SneakyThrows; -import org.apache.pdfbox.Loader; -import org.apache.pdfbox.pdmodel.PDDocument; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.ClassPathResource; - -import java.awt.geom.Rectangle2D; -import java.io.IOException; -import java.util.*; -import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; - -import javax.sound.midi.SysexMessage; public class PdfSegmentationServiceTest extends AbstractTest { @@ -67,6 +76,18 @@ public class PdfSegmentationServiceTest extends AbstractTest { } + @Test + public void tablesToHtmlDebugger() throws IOException { + + ClassPathResource pdfFileResource = new ClassPathResource("files/SinglePages/A20622A izRMS (CZ) fRR Part B9_Page185.pdf"); + + ClassificationDocument document = buildClassificationDocument(Loader.loadPDF(pdfFileResource.getFile())); + + toHtml(document, "/tmp/A20622A izRMS (CZ) fRR Part B9_Page185.html"); + + } + + @Test @SneakyThrows public void testMapping() { @@ -157,7 +178,7 @@ public class PdfSegmentationServiceTest extends AbstractTest { } - @Test + @Test // Non-sense test public void testDoc56Page170() throws IOException { ClassPathResource pdfFileResource = new ClassPathResource("files/SinglePages/56 Fludioxonil_RAR_12_Volume_3CA_B-7_2018-02-21_Page170.pdf"); @@ -168,8 +189,25 @@ public class PdfSegmentationServiceTest extends AbstractTest { validateTable(document, 0, 1, 1, 0, 0); validateTable(document, 1, 2, 2, 0, 0); - validateTable(document, 2, 7, 20, 0, 0); - validateTable(document, 3, 8, 31, 0, 0); + validateTable(document, 2, 6, 20, 0, 0); + validateTable(document, 3, 7, 31, 0, 0); + + } + + + @Test + public void testDoc211() throws IOException { + + ClassPathResource pdfFileResource = new ClassPathResource("files/211.pdf"); + + ClassificationDocument document = buildClassificationDocument(Loader.loadPDF(pdfFileResource.getFile())); + + validateTableSize(document, 4); + + validateTable(document, 0, 5, 4, 0, 0); + validateTable(document, 1, 5, 15, 14, 0); + validateTable(document, 2, 5, 14, 11, 0); + validateTable(document, 3, 5, 3, 0, 0); } @@ -222,6 +260,8 @@ public class PdfSegmentationServiceTest extends AbstractTest { ClassificationDocument document = buildClassificationDocument(Loader.loadPDF(pdfFileResource.getFile())); + toHtml(document, "/tmp/html.html"); + validateTableSize(document, 4); validateTable(document, 0, 3, 2, 0, 0); @@ -233,17 +273,29 @@ public class PdfSegmentationServiceTest extends AbstractTest { @Test + @Disabled // FIXME Fake Redactions leads to more cells, no solution for this currently public void testDocA20622APartB9Page185() throws IOException { ClassPathResource pdfFileResource = new ClassPathResource("files/SinglePages/A20622A izRMS (CZ) fRR Part B9_Page185.pdf"); ClassificationDocument document = buildClassificationDocument(Loader.loadPDF(pdfFileResource.getFile())); - validateTableSize(document, 2); + validateTableSize(document, 1); - validateTable(document, 0, 5, 5, 0, 0); - validateTable(document, 1, 11, 9, 0, 0); + validateTable(document, 0, 7, 4, 0, 0); + } + + @Test + public void testDocA20622APartB9Page185FixedDoc() throws IOException { + + ClassPathResource pdfFileResource = new ClassPathResource("files/SinglePages/A20622A izRMS (CZ) fRR Part B9_Page185_fixed.pdf"); + + ClassificationDocument document = buildClassificationDocument(Loader.loadPDF(pdfFileResource.getFile())); + + validateTableSize(document, 1); + + validateTable(document, 0, 7, 4, 0, 0); } @@ -467,7 +519,7 @@ public class PdfSegmentationServiceTest extends AbstractTest { validateTableSize(document, 1); - validateTable(document, 0, 9, 5, 0, 0); + validateTable(document, 0, 9, 5, 2, 0); } @@ -486,6 +538,28 @@ public class PdfSegmentationServiceTest extends AbstractTest { } + @SneakyThrows + private void toHtml(ClassificationDocument document, String filename) { + + var tables = document.getSections().stream().flatMap(paragraph -> paragraph.getTables().stream()).toList(); + StringBuilder sb = new StringBuilder(); + + int currentPage = 1; + for (var table : tables) { + if (currentPage != table.getPage()) { + currentPage = table.getPage(); + sb.append("---------------------- Page ").append(currentPage).append("--------------\n"); + } + sb.append("\n\n"); + sb.append(table.getTextAsHtml()); + } + + try (FileOutputStream fileOutputStream = new FileOutputStream(Path.of(filename).toFile())) { + fileOutputStream.write(sb.toString().getBytes()); + } + } + + private void validateTable(ClassificationDocument document, int tableIndex, int colCount, int rowCount, int emptyCellsCountCorrect, int emptyCellsCountIncorrect) { TablePageBlock table = document.getSections().stream().flatMap(paragraph -> paragraph.getTables().stream()).toList().get(tableIndex); diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java index b1353d5..c674495 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java @@ -1,25 +1,16 @@ package com.knecon.fforesight.service.layoutparser.server.services; -import java.io.File; -import java.io.FileOutputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import javax.print.Doc; - import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.junit.jupiter.api.Test; import org.springframework.core.io.ClassPathResource; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.iqser.red.commons.jackson.ObjectMapperFactory; import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentData; import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructure; import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType; @@ -35,7 +26,6 @@ import com.knecon.fforesight.service.layoutparser.processor.services.RulingClean import com.knecon.fforesight.service.layoutparser.processor.services.factory.DocumentGraphFactory; import com.knecon.fforesight.service.layoutparser.processor.services.mapper.DocumentDataMapper; import com.knecon.fforesight.service.layoutparser.processor.services.mapper.PropertiesMapper; -import com.knecon.fforesight.service.layoutparser.processor.services.parsing.PDFLinesTextStripper; import com.knecon.fforesight.service.layoutparser.processor.services.visualization.LayoutGridService; import com.knecon.fforesight.service.layoutparser.processor.services.visualization.ViewerDocumentService; import com.knecon.fforesight.service.layoutparser.server.utils.BuildDocumentTest; @@ -57,26 +47,27 @@ public class RulingCleaningServiceTest extends BuildDocumentTest { List cleanRulingsPerPage = new LinkedList<>(); writeJsons(Path.of(fileName)); for (PageContents pageContent : pageContents) { - cleanRulingsPerPage.add(rulingCleaningService.getCleanRulings(Collections.emptyList(), pageContent.getRulings(), 8, 1)); + cleanRulingsPerPage.add(rulingCleaningService.getCleanRulings(Collections.emptyList(), pageContent.getRulings())); } PdfDraw.drawLinesPerPage(fileName, pageContents.stream().map(PageContents::getRulings).toList(), lineFileName); - } + @Test @SneakyThrows public void testTableExtractionSingle() { - String filename ="C:\\Users\\YannikHampe\\repos\\layout-parser\\layoutparser-service\\layoutparser-service-server\\src\\test\\resources\\files\\SinglePages\\24 - SYN549522 - Acute Oral Toxicity - Rats_Page17.pdf"; + + String filename = "C:\\Users\\YannikHampe\\repos\\layout-parser\\layoutparser-service\\layoutparser-service-server\\src\\test\\resources\\files\\SinglePages\\24 - SYN549522 - Acute Oral Toxicity - Rats_Page17.pdf"; writeJsons(Path.of(filename)); } + @Test @SneakyThrows public void testTableExtraction() { - LayoutGridService layoutGridService = new LayoutGridService(); ViewerDocumentService viewerDocumentService = new ViewerDocumentService(layoutGridService); @@ -92,64 +83,67 @@ public class RulingCleaningServiceTest extends BuildDocumentTest { } } + @SneakyThrows private void writeJsons(Path filename) { - Document documentGraphBefore = DocumentGraphFactory.buildDocumentGraph(layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER, - Loader.loadPDF(filename.toFile()), - new ImageServiceResponse(), - new TableServiceResponse())); - Document documentGraphAfter = DocumentGraphFactory.buildDocumentGraph(layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER, - Loader.loadPDF(filename.toFile()), - new ImageServiceResponse(), - new TableServiceResponse())); + Document documentGraphBefore = DocumentGraphFactory.buildDocumentGraph(layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER, + Loader.loadPDF(filename.toFile()), + new ImageServiceResponse(), + new TableServiceResponse())); + Document documentGraphAfter = DocumentGraphFactory.buildDocumentGraph(layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER, + Loader.loadPDF(filename.toFile()), + new ImageServiceResponse(), + new TableServiceResponse())); DocumentData documentDataBefore = DocumentDataMapper.toDocumentData(documentGraphBefore); DocumentData documentDataAfter = DocumentDataMapper.toDocumentData(documentGraphAfter); - if(!compareStructures(documentDataBefore.getDocumentStructure(), documentDataAfter.getDocumentStructure(), filename.getFileName().toString())) { - String tmpFileNameBefore = "C:/Users/YANNIK~1/AppData/Local/Temp/before."+filename.getFileName().toString();; - System.out.println(tmpFileNameBefore); - try (PDDocument pdDocument = Loader.loadPDF(filename.toFile())) { + if (!compareStructures(documentDataBefore.getDocumentStructure(), documentDataAfter.getDocumentStructure(), filename.getFileName().toString())) { + String tmpFileNameBefore = "C:/Users/YANNIK~1/AppData/Local/Temp/before." + filename.getFileName().toString(); + ; + System.out.println(tmpFileNameBefore); + try (PDDocument pdDocument = Loader.loadPDF(filename.toFile())) { PdfDraw.drawDocumentGraph(pdDocument, documentGraphBefore); pdDocument.save(tmpFileNameBefore); - } - String tmpFileNameAfter = "C:/Users/YANNIK~1/AppData/Local/Temp/after."+filename.getFileName().toString();; - System.out.println(tmpFileNameAfter); - try (PDDocument pdDocument = Loader.loadPDF(filename.toFile())) { - PdfDraw.drawDocumentGraph(pdDocument, documentGraphAfter); - pdDocument.save(tmpFileNameAfter); - - } } + String tmpFileNameAfter = "C:/Users/YANNIK~1/AppData/Local/Temp/after." + filename.getFileName().toString(); + ; + System.out.println(tmpFileNameAfter); + try (PDDocument pdDocument = Loader.loadPDF(filename.toFile())) { + PdfDraw.drawDocumentGraph(pdDocument, documentGraphAfter); + pdDocument.save(tmpFileNameAfter); + + } + } } + + @SneakyThrows private boolean compareStructures(DocumentStructure structure1, DocumentStructure structure2, String pdfName) { - - List listStructure1 = structure1 - .streamAllEntries() + List listStructure1 = structure1.streamAllEntries() .filter(entryData -> entryData.getType().equals(NodeType.TABLE)) .map(DocumentStructure.EntryData::getProperties) .map(properties -> { var builder = Table.builder(); PropertiesMapper.parseTableProperties(properties, builder); return builder.build(); - }).toList(); + }) + .toList(); - List listStructure2 = structure2 - .streamAllEntries() + List listStructure2 = structure2.streamAllEntries() .filter(entryData -> entryData.getType().equals(NodeType.TABLE)) .map(DocumentStructure.EntryData::getProperties) .map(properties -> { var builder = Table.builder(); PropertiesMapper.parseTableProperties(properties, builder); return builder.build(); - }).toList(); + }) + .toList(); - - for(int i = 0; i < listStructure1.size(); i++) { + for (int i = 0; i < listStructure1.size(); i++) { Table tableNode1 = (Table) listStructure1.get(i); Table tableNode2 = (Table) listStructure2.get(i); - if(tableNode1.getNumberOfRows() != tableNode2.getNumberOfRows() || tableNode1.getNumberOfCols() != tableNode2.getNumberOfCols()) { + if (tableNode1.getNumberOfRows() != tableNode2.getNumberOfRows() || tableNode1.getNumberOfCols() != tableNode2.getNumberOfCols()) { return false; } } diff --git a/layoutparser-service/layoutparser-service-server/src/test/resources/files/SinglePages/A20622A izRMS (CZ) fRR Part B9_Page185_fixed.pdf b/layoutparser-service/layoutparser-service-server/src/test/resources/files/SinglePages/A20622A izRMS (CZ) fRR Part B9_Page185_fixed.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9610cb59768b0d4225ded0a6841402e10e274c6b GIT binary patch literal 151195 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
H`hE6(9kNeMdsj%w8qvS6f2{v# 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%0Db4${is@-gH8nD0h1nK0 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

uw7T8s;=qYZlNM}D>y|| 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 Date: Thu, 16 Nov 2023 07:51:08 +0100 Subject: [PATCH 6/9] * fixed tests --- .../server/graph/ViewerDocumentTest.java | 46 +------------------ .../services/RulingCleaningServiceTest.java | 17 ++----- 2 files changed, 5 insertions(+), 58 deletions(-) diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ViewerDocumentTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ViewerDocumentTest.java index af0de8f..e8dd8d6 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ViewerDocumentTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ViewerDocumentTest.java @@ -45,8 +45,8 @@ public class ViewerDocumentTest extends BuildDocumentTest { @SneakyThrows public void testViewerDocument() { - String fileName = "files/2Tables.pdf"; - String tmpFileName = "C:/Users/YANNIK~1/AppData/Local/Temp/2Tables.lines.pdf"; + String fileName = "files/bdr/notMergedParagraphs.pdf"; + String tmpFileName = "/tmp/" + Path.of(fileName).getFileName() + "_VIEWER.pdf"; LayoutGridService layoutGridService = new LayoutGridService(); ViewerDocumentService viewerDocumentService = new ViewerDocumentService(layoutGridService); Document document = buildGraph(fileName, LayoutParsingType.TAAS); @@ -55,48 +55,6 @@ public class ViewerDocumentTest extends BuildDocumentTest { } } - @Test - @SneakyThrows - public void testTableViewerDocument() { - - String fileName = "C:\\Users\\YannikHampe\\repos\\layout-parser\\layoutparser-service\\layoutparser-service-server\\src\\test\\resources\\files\\SinglePages\\VV-931175_Page1.pdf"; - String tmpFileName = "C:/Users/YANNIK~1/AppData/Local/Temp/page1.lines.pdf"; - Document documentGraph = DocumentGraphFactory.buildDocumentGraph(layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER, - Loader.loadPDF(Path.of(fileName).toFile()), - new ImageServiceResponse(), - new TableServiceResponse())); - LayoutGridService layoutGridService = new LayoutGridService(); - ViewerDocumentService viewerDocumentService = new ViewerDocumentService(layoutGridService); - try (var pdDocument = Loader.loadPDF(Path.of(fileName).toFile()); var out = new FileOutputStream(tmpFileName)) { - viewerDocumentService.createViewerDocument(pdDocument, documentGraph, out, true); - } - //durch rows - DocumentData documentData = DocumentDataMapper.toDocumentData(documentGraph); - int emptyCellCount = 0; - List listStructure2 = documentData.getDocumentStructure() - .streamAllEntries() - .filter(entryData -> entryData.getType().equals(NodeType.TABLE)) - .map(DocumentStructure.EntryData::getProperties) - .map(properties -> { - var builder = Table.builder(); - PropertiesMapper.parseTableProperties(properties, builder); - return builder.build(); - }).toList(); - for(int i = 0; i < listStructure2.size(); i++) { - emptyCellCount = ((Table) listStructure2.get(i)).getEmptyCells(); - } - - System.out.println("Empty cells "+emptyCellCount); - - ClassificationDocument document = buildClassificationDocument(Loader.loadPDF(Path.of(fileName).toFile())); - TablePageBlock table = document.getSections().stream().flatMap(paragraph -> paragraph.getTables().stream()).toList().get(0); - int emptyCellsFoundFound = table.getRows().stream().flatMap(List::stream).toList().stream().filter(f -> f.toString().equals("")).toList().size(); - for (List row : table.getRows()) { - System.out.println(row.toString()); - } - System.out.println("Actual number of empty rows: "+emptyCellsFoundFound); - } - public ClassificationDocument buildClassificationDocument(PDDocument originDocument) { ClassificationDocument classificationDocument = layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER, diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java index c674495..b8adf9f 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java @@ -40,26 +40,15 @@ public class RulingCleaningServiceTest extends BuildDocumentTest { @SneakyThrows public void textRulingExtraction() { - String fileName = "/files/102 S-Metolachlor_RAR_02_Volume_2_2018-09-06.pdf"; - String lineFileName = "C:/Users/YANNIK~1/AppData/Local/Temp/102 S-Metolachlor_RAR_02_Volume_2_2018-09-06.after.pdf"; + String fileName = "files/211.pdf"; + String lineFileName = "/tmp/" + Path.of(fileName).getFileName().toString() + "_LINES.pdf"; List pageContents = PageContentExtractor.getSortedPageContents(fileName); RulingCleaningService rulingCleaningService = new RulingCleaningService(); + PdfDraw.drawLinesPerPage(fileName, pageContents.stream().map(PageContents::getRulings).toList(), lineFileName); List cleanRulingsPerPage = new LinkedList<>(); - writeJsons(Path.of(fileName)); for (PageContents pageContent : pageContents) { cleanRulingsPerPage.add(rulingCleaningService.getCleanRulings(Collections.emptyList(), pageContent.getRulings())); } - PdfDraw.drawLinesPerPage(fileName, pageContents.stream().map(PageContents::getRulings).toList(), lineFileName); - - } - - - @Test - @SneakyThrows - public void testTableExtractionSingle() { - - String filename = "C:\\Users\\YannikHampe\\repos\\layout-parser\\layoutparser-service\\layoutparser-service-server\\src\\test\\resources\\files\\SinglePages\\24 - SYN549522 - Acute Oral Toxicity - Rats_Page17.pdf"; - writeJsons(Path.of(filename)); } From b25d46291acc62ea20e263c49290646407545b4b Mon Sep 17 00:00:00 2001 From: yhampe Date: Thu, 16 Nov 2023 08:12:47 +0100 Subject: [PATCH 7/9] * checkstyle --- .../server/services/RulingCleaningServiceTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java index b8adf9f..ae7e418 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/RulingCleaningServiceTest.java @@ -88,15 +88,11 @@ public class RulingCleaningServiceTest extends BuildDocumentTest { DocumentData documentDataAfter = DocumentDataMapper.toDocumentData(documentGraphAfter); if (!compareStructures(documentDataBefore.getDocumentStructure(), documentDataAfter.getDocumentStructure(), filename.getFileName().toString())) { String tmpFileNameBefore = "C:/Users/YANNIK~1/AppData/Local/Temp/before." + filename.getFileName().toString(); - ; - System.out.println(tmpFileNameBefore); try (PDDocument pdDocument = Loader.loadPDF(filename.toFile())) { PdfDraw.drawDocumentGraph(pdDocument, documentGraphBefore); pdDocument.save(tmpFileNameBefore); } String tmpFileNameAfter = "C:/Users/YANNIK~1/AppData/Local/Temp/after." + filename.getFileName().toString(); - ; - System.out.println(tmpFileNameAfter); try (PDDocument pdDocument = Loader.loadPDF(filename.toFile())) { PdfDraw.drawDocumentGraph(pdDocument, documentGraphAfter); pdDocument.save(tmpFileNameAfter); From e203210ade958c286f4ca7f2b80d44358bd932a3 Mon Sep 17 00:00:00 2001 From: yhampe Date: Thu, 16 Nov 2023 08:23:58 +0100 Subject: [PATCH 8/9] * removed not needed properties --- .../internal/api/data/redaction/DocumentStructure.java | 1 - .../layoutparser/processor/model/graph/nodes/Table.java | 2 -- .../processor/services/mapper/PropertiesMapper.java | 3 --- 3 files changed, 6 deletions(-) diff --git a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java index 7806960..6852896 100644 --- a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java +++ b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java @@ -29,7 +29,6 @@ public class DocumentStructure { @Schema(description = "Object containing the extra field names, a table has in its properties field.") public static class TableProperties { - public static final String FIRST_PAGE ="firstPage"; public static final String NUMBER_OF_ROWS = "numberOfRows"; public static final String NUMBER_OF_COLS = "numberOfCols"; diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java index fb6e7b7..d62b4cf 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java @@ -34,8 +34,6 @@ public class Table implements SemanticNode { int numberOfRows; int numberOfCols; - int firstpage; - int emptyCells; TextBlock textBlock; @Builder.Default diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java index 3bbe832..329bd40 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java @@ -46,8 +46,6 @@ public class PropertiesMapper { public static Map buildTableProperties(Table table) { Map properties = new HashMap<>(); - Page page = table.getFirstPage(); - properties.put(DocumentStructure.TableProperties.FIRST_PAGE, String.valueOf(page.getNumber())); properties.put(DocumentStructure.TableProperties.NUMBER_OF_ROWS, String.valueOf(table.getNumberOfRows())); properties.put(DocumentStructure.TableProperties.NUMBER_OF_COLS, String.valueOf(table.getNumberOfCols())); return properties; @@ -73,7 +71,6 @@ public class PropertiesMapper { public static void parseTableProperties(Map properties, Table.TableBuilder builder) { - builder.firstpage(Integer.parseInt(properties.get(DocumentStructure.TableProperties.FIRST_PAGE))); builder.numberOfRows(Integer.parseInt(properties.get(DocumentStructure.TableProperties.NUMBER_OF_ROWS))); builder.numberOfCols(Integer.parseInt(properties.get(DocumentStructure.TableProperties.NUMBER_OF_COLS))); } From 1316a067fec77ebcc287fbd68e1c243184a47fad Mon Sep 17 00:00:00 2001 From: yhampe Date: Thu, 16 Nov 2023 08:51:12 +0100 Subject: [PATCH 9/9] * removed double chechking for height of cell --- .../layoutparser/processor/model/table/TablePageBlock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java index 45b756c..1295424 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java @@ -278,7 +278,7 @@ public class TablePageBlock extends AbstractPageBlock { public boolean intersects(Cell cell1, Cell cell2) { - if (cell1.getHeight() <= 0 || cell1.getHeight() <= 0 || cell2.getHeight() <= 0 || cell2.getHeight() <= 0) { + if (cell1.getHeight() <= 0 || cell2.getHeight() <= 0) { return false; } double x0 = cell1.getX() + 2;