diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/visualization/LayoutGridService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/visualization/LayoutGridService.java index 8b9bc3e..5ca5707 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/visualization/LayoutGridService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/visualization/LayoutGridService.java @@ -8,10 +8,13 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.springframework.stereotype.Service; @@ -33,7 +36,7 @@ public class LayoutGridService { private static final Color INNER_LINES_COLOR = new Color(255, 175, 175); private static final Color PARAGRAPH_COLOR = new Color(70, 130, 180); public static final Color TABLE_COLOR = new Color(102, 205, 170); - public static final Color SECTION_COLOR = new Color(23, 23, 23); + public static final Color SECTION_COLOR = new Color(50, 50, 50); public static final Color HEADLINE_COLOR = new Color(162, 56, 56); public static final Color HEADER_COLOR = new Color(171, 131, 6); public static final Color IMAGE_COLOR = new Color(253, 63, 146); @@ -60,7 +63,7 @@ public class LayoutGridService { } if (semanticNode.getType().equals(NodeType.TABLE)) { Table table = (Table) semanticNode; - addInnerTableLines(table, layoutGrid, INNER_LINES_COLOR); + addInnerTableLines(table, layoutGrid); } }); @@ -68,26 +71,51 @@ public class LayoutGridService { } - private void addInnerTableLines(Table table, LayoutGrid layoutGrid, Color color) { + private void addInnerTableLines(Table table, LayoutGrid layoutGrid) { if (table.getNumberOfCols() < 1 || table.getNumberOfRows() < 1) { return; } - // only draw inner lines -> remove first values - List xs = table.streamRow(0).map(TableCell::getBBox).map(map -> map.values().stream().findAny().get()).map(RectangularShape::getMinX).collect(Collectors.toList()); - xs.remove(0); - List ys = table.streamCol(0).map(TableCell::getBBox).map(map -> map.values().stream().findAny().get()).map(RectangularShape::getMaxY).collect(Collectors.toList()); - ys.remove(0); - Rectangle2D tableBBox = table.getBBox().get(table.getFirstPage()); - List coloredLines = layoutGrid.getVisualizationsPerPages().get(table.getFirstPage().getNumber() - 1).getColoredLines(); - xs.forEach(x -> { - Line2D line = new Line2D.Double(new Point2D.Double(x, tableBBox.getMaxY()), new Point2D.Double(x, tableBBox.getMinY())); - coloredLines.add(new ColoredLine(line, color)); - }); - ys.forEach(y -> { - Line2D line = new Line2D.Double(new Point2D.Double(tableBBox.getMinX(), y), new Point2D.Double(tableBBox.getMaxX(), y)); - coloredLines.add(new ColoredLine(line, color)); - }); + for (Page page : table.getPages()) { + Optional optionalFirstRowOnPage = table.streamCol(0).filter(tableCell -> tableCell.isOnPage(page.getNumber())).map(TableCell::getRow).findFirst(); + if (optionalFirstRowOnPage.isEmpty()) { + continue; + } + int firstRowOnPage = optionalFirstRowOnPage.get(); + Stream xStream = switch (page.getRotation()) { + case 90 -> streamBBoxOfCellsOnPage(table.streamCol(0), page).map(RectangularShape::getMinX); + case 180 -> streamBBoxOfCellsOnPage(table.streamRow(firstRowOnPage), page).map(RectangularShape::getMaxX); + case 270 -> streamBBoxOfCellsOnPage(table.streamCol(0), page).map(RectangularShape::getMaxX); + default -> streamBBoxOfCellsOnPage(table.streamRow(firstRowOnPage), page).map(RectangularShape::getMinX); + }; + List xs = xStream.collect(Collectors.toList()); + xs.remove(0); + Stream yStream = switch (page.getRotation()) { + case 90 -> streamBBoxOfCellsOnPage(table.streamRow(firstRowOnPage), page).map(RectangularShape::getMinY); + case 180 -> streamBBoxOfCellsOnPage(table.streamCol(0), page).map(RectangularShape::getMinY); + case 270 -> streamBBoxOfCellsOnPage(table.streamRow(firstRowOnPage), page).map(RectangularShape::getMaxY); + default -> streamBBoxOfCellsOnPage(table.streamCol(0), page).map(RectangularShape::getMaxY); + }; + List ys = yStream.collect(Collectors.toList()); + ys.remove(0); + + Rectangle2D tableBBox = table.getBBox().get(table.getFirstPage()); + List coloredLines = layoutGrid.getVisualizationsPerPages().get(page.getNumber() - 1).getColoredLines(); + xs.forEach(x -> { + Line2D line = new Line2D.Double(new Point2D.Double(x, tableBBox.getMaxY()), new Point2D.Double(x, tableBBox.getMinY())); + coloredLines.add(new ColoredLine(line, INNER_LINES_COLOR)); + }); + ys.forEach(y -> { + Line2D line = new Line2D.Double(new Point2D.Double(tableBBox.getMinX(), y), new Point2D.Double(tableBBox.getMaxX(), y)); + coloredLines.add(new ColoredLine(line, INNER_LINES_COLOR)); + }); + } + } + + + private static Stream streamBBoxOfCellsOnPage(Stream table, Page page) { + + return table.filter(tableCell -> tableCell.isOnPage(page.getNumber())).map(TableCell::getBBox).map(bBoxMap -> bBoxMap.get(page)); } @@ -95,17 +123,16 @@ public class LayoutGridService { Map bBoxMap = semanticNode.getBBox(); List subSections = semanticNode.streamAllSubNodesOfType(NodeType.SECTION).toList(); + Page firstPage = semanticNode.getFirstPage(); if (!subSections.isEmpty()) { - Page firstPage = semanticNode.getFirstPage(); addPlacedText(firstPage, bBoxMap.get(firstPage), buildTreeIdString(semanticNode), layoutGrid); } else { bBoxMap.forEach(((page, textBBox) -> addPlacedText(page, textBBox, buildTreeIdString(semanticNode), layoutGrid))); } if (bBoxMap.values().size() == 1) { - Rectangle2D r = RectangleTransformations.pad(bBoxMap.values().stream().findFirst().get(), LINE_WIDTH, LINE_WIDTH); - int pageNumber = bBoxMap.keySet().stream().findFirst().get().getNumber() - 1; - List coloredLines = layoutGrid.getVisualizationsPerPages().get(pageNumber).getColoredLines(); - List lines = createLinesFromRectangle(r); + Rectangle2D r = RectangleTransformations.pad(bBoxMap.get(firstPage), LINE_WIDTH, LINE_WIDTH); + List coloredLines = layoutGrid.getVisualizationsPerPages().get(firstPage.getNumber() - 1).getColoredLines(); + List lines = createLinesFromRectangle(r, firstPage.getRotation()); // add string to top line var firstLine = lines.remove(0); coloredLines.add(new ColoredLine(firstLine, color)); @@ -115,7 +142,7 @@ public class LayoutGridService { return; } List pagesInOrder = bBoxMap.keySet().stream().sorted(Comparator.comparingInt(Page::getNumber)).collect(Collectors.toList()); - var firstPage = pagesInOrder.remove(0); + pagesInOrder.remove(0); addLinesForFirstPageOfSection(semanticNode, color, firstPage, layoutGrid); var lastPage = pagesInOrder.remove(pagesInOrder.size() - 1); addLinesForLastPageOfSection(semanticNode, color, lastPage, layoutGrid); @@ -127,8 +154,14 @@ public class LayoutGridService { private void addPlacedText(Page page, Rectangle2D textBBox, String s, LayoutGrid layoutGrid) { + Point2D.Float upperLeftCorner = switch (page.getRotation()) { + case 90 -> new Point2D.Float((float) (textBBox.getMinX()), (float) textBBox.getMinY()); + case 180 -> new Point2D.Float((float) (textBBox.getMaxX()), (float) textBBox.getMinY()); + case 270 -> new Point2D.Float((float) (textBBox.getMaxX()), (float) textBBox.getMaxY()); + default -> new Point2D.Float((float) (textBBox.getMinX()), (float) textBBox.getMaxY()); + }; var placedTexts = layoutGrid.getVisualizationsPerPages().get(page.getNumber() - 1).getPlacedTexts(); - placedTexts.add(new PlacedText(s, new Point2D.Float((float) (textBBox.getMinX()), (float) textBBox.getMaxY()))); + placedTexts.add(new PlacedText(s, upperLeftCorner)); } @@ -136,7 +169,7 @@ public class LayoutGridService { List coloredLines = layoutGrid.getVisualizationsPerPages().get(middlePage.getNumber() - 1).getColoredLines(); Rectangle2D r = RectangleTransformations.pad(semanticNode.getBBox().get(middlePage), LINE_WIDTH, LINE_WIDTH); - var midPageLines = createLinesFromRectangle(r); + var midPageLines = createLinesFromRectangle(r, middlePage.getRotation()); // remove top line midPageLines.remove(0); // remove top line @@ -154,7 +187,7 @@ public class LayoutGridService { List coloredLines = layoutGrid.getVisualizationsPerPages().get(lastPage.getNumber() - 1).getColoredLines(); Rectangle2D r = RectangleTransformations.pad(semanticNode.getBBox().get(lastPage), LINE_WIDTH, LINE_WIDTH); - var lastPageLines = createLinesFromRectangle(r); + var lastPageLines = createLinesFromRectangle(r, lastPage.getRotation()); // remove top line lastPageLines.remove(0); // add string to left line @@ -170,7 +203,7 @@ public class LayoutGridService { List coloredLines = layoutGrid.getVisualizationsPerPages().get(firstPage.getNumber() - 1).getColoredLines(); Rectangle2D r = RectangleTransformations.pad(semanticNode.getBBox().get(firstPage), LINE_WIDTH, LINE_WIDTH); - var firstPageLines = createLinesFromRectangle(r); + var firstPageLines = createLinesFromRectangle(r, firstPage.getRotation()); // remove bottom line firstPageLines.remove(2); // add string to top line @@ -196,8 +229,9 @@ public class LayoutGridService { | | D|__________________| C The returned List are the lines [AB, BC, DC, AD] + The List is reordered, such that the order of the returned lines are always as viewed on the page. */ - private List createLinesFromRectangle(Rectangle2D r) { + private List createLinesFromRectangle(Rectangle2D r, int pageRotation) { // +0.5 to join the lines List lines = new ArrayList<>(4); float lineWidthCorrection = LINE_WIDTH * 0.5f; @@ -213,7 +247,23 @@ public class LayoutGridService { lines.add(new Line2D.Float(b, c)); lines.add(new Line2D.Float(d1, c1)); lines.add(new Line2D.Float(a, d)); - return lines; + + return switch (pageRotation) { + case 90 -> { + Collections.rotate(lines, 1); + yield lines; + } + case 180 -> { + Collections.rotate(lines, 2); + yield lines; + } + case 270 -> { + Collections.rotate(lines, 3); + yield lines; + } + + default -> lines; + }; } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/visualization/ViewerDocumentService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/visualization/ViewerDocumentService.java index 820493d..a9801c9 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/visualization/ViewerDocumentService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/visualization/ViewerDocumentService.java @@ -42,7 +42,7 @@ public class ViewerDocumentService { private static final String layerName = "Layout grid"; private static final int FONT_SIZE = 10; - public static final float LINE_WIDTH = 1.5f; + public static final float LINE_WIDTH = 1f; private final LayoutGridService layoutGridService; @@ -118,6 +118,7 @@ public class ViewerDocumentService { dictionariesToUpdate.add(pdPage.getCOSObject()); dictionariesToUpdate.add(pdPage.getResources().getCOSObject()); } + dictionariesToUpdate.add(pdDocument.getDocumentInformation().getCOSObject()); pdDocument.saveIncremental(outputStream, dictionariesToUpdate); log.info("Saved Viewer Document"); }