RED-6369: Rules Refactor
*fixed DocumentData Mapping
This commit is contained in:
parent
b6307759fb
commit
d804f0d0a2
@ -11,11 +11,14 @@ import lombok.experimental.FieldDefaults;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
||||||
public class AtomicTextBlockData {
|
public class AtomicTextBlockData {
|
||||||
|
|
||||||
Long id;
|
Long id;
|
||||||
|
Long page;
|
||||||
String searchText;
|
String searchText;
|
||||||
int start;
|
int start;
|
||||||
int end;
|
int end;
|
||||||
int[] lineBreaks;
|
int[] lineBreaks;
|
||||||
int[] stringIdxToPositionIdx;
|
int[] stringIdxToPositionIdx;
|
||||||
float[][] positions;
|
float[][] positions;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import static java.lang.String.format;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import javax.management.openmbean.InvalidKeyException;
|
import javax.management.openmbean.InvalidKeyException;
|
||||||
|
|
||||||
@ -26,7 +28,6 @@ public class TableOfContentsData {
|
|||||||
|
|
||||||
public EntryData get(List<Integer> tocId) {
|
public EntryData get(List<Integer> tocId) {
|
||||||
|
|
||||||
|
|
||||||
if (tocId.size() < 1) {
|
if (tocId.size() < 1) {
|
||||||
throw new InvalidKeyException(format("Section Identifier: \"%s\" is not valid.", tocId));
|
throw new InvalidKeyException(format("Section Identifier: \"%s\" is not valid.", tocId));
|
||||||
}
|
}
|
||||||
@ -38,14 +39,51 @@ public class TableOfContentsData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Stream<EntryData> streamAllEntries() {
|
||||||
|
|
||||||
|
return entries.stream().flatMap(TableOfContentsData::flatten);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static List<Integer> getIds(String idsAsString) {
|
private static List<Integer> getIds(String idsAsString) {
|
||||||
|
|
||||||
return Arrays.stream(idsAsString.split("\\.")).map(Integer::valueOf).toList();
|
return Arrays.stream(idsAsString.split("\\.")).map(Integer::valueOf).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
|
||||||
|
return String.join("\n", streamAllEntries().map(EntryData::toString).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Stream<EntryData> flatten(EntryData entry) {
|
||||||
|
|
||||||
|
return Stream.concat(Stream.of(entry), entry.subEntries().stream().flatMap(TableOfContentsData::flatten));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Builder
|
@Builder
|
||||||
public record EntryData(int[] tocId, List<EntryData> subEntries, NodeType type, Long[] atomicTextBlocks, Long[] pages, int numberOnPage) {
|
public record EntryData(int[] tocId, List<EntryData> subEntries, NodeType type, Long[] atomicTextBlocks, Long[] pages, int numberOnPage, Map<String, String> properties) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("[");
|
||||||
|
for (int i : tocId) {
|
||||||
|
sb.append(i);
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
sb.delete(sb.length() - 1, sb.length());
|
||||||
|
sb.append("]: ");
|
||||||
|
|
||||||
|
sb.append(type);
|
||||||
|
sb.append(" atbs = ");
|
||||||
|
sb.append(atomicTextBlocks.length);
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
package com.iqser.red.service.redaction.v1.server.document.data.mapper;
|
package com.iqser.red.service.redaction.v1.server.document.data.mapper;
|
||||||
|
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@ -11,7 +13,11 @@ import com.iqser.red.service.redaction.v1.server.document.data.PageData;
|
|||||||
import com.iqser.red.service.redaction.v1.server.document.data.TableOfContentsData;
|
import com.iqser.red.service.redaction.v1.server.document.data.TableOfContentsData;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.DocumentGraph;
|
import com.iqser.red.service.redaction.v1.server.document.graph.DocumentGraph;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.TableOfContents;
|
import com.iqser.red.service.redaction.v1.server.document.graph.TableOfContents;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.document.graph.entity.ImageNode;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.document.graph.factory.RectangleTransformations;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.PageNode;
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.PageNode;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.TableCellNode;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.TableNode;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.textblock.AtomicTextBlock;
|
import com.iqser.red.service.redaction.v1.server.document.graph.textblock.AtomicTextBlock;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.textblock.TextBlock;
|
import com.iqser.red.service.redaction.v1.server.document.graph.textblock.TextBlock;
|
||||||
|
|
||||||
@ -40,23 +46,68 @@ public class DocumentDataMapper {
|
|||||||
private TableOfContentsData.EntryData toEntryData(TableOfContents.Entry entry) {
|
private TableOfContentsData.EntryData toEntryData(TableOfContents.Entry entry) {
|
||||||
|
|
||||||
Long[] atomicTextBlocks;
|
Long[] atomicTextBlocks;
|
||||||
|
|
||||||
if (entry.node().isTerminal()) {
|
if (entry.node().isTerminal()) {
|
||||||
atomicTextBlocks = toAtomicTextBlockIds(entry.node().getTerminalTextBlock());
|
atomicTextBlocks = toAtomicTextBlockIds(entry.node().getTerminalTextBlock());
|
||||||
} else {
|
} else {
|
||||||
atomicTextBlocks = new Long[]{-1L};
|
atomicTextBlocks = new Long[]{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String> properties = switch (entry.type()) {
|
||||||
|
case TABLE -> buildTableProperties((TableNode) entry.node());
|
||||||
|
case TABLE_CELL -> buildTableCellProperties((TableCellNode) entry.node());
|
||||||
|
case IMAGE -> buildImageProperties((ImageNode) entry.node());
|
||||||
|
default -> new HashMap<>();
|
||||||
|
};
|
||||||
|
|
||||||
return TableOfContentsData.EntryData.builder()
|
return TableOfContentsData.EntryData.builder()
|
||||||
.tocId(toPrimitiveIntArray(entry.tocId()))
|
.tocId(toPrimitiveIntArray(entry.tocId()))
|
||||||
.subEntries(entry.children().stream().map(this::toEntryData).toList())
|
.subEntries(entry.children().stream().map(this::toEntryData).toList())
|
||||||
.type(entry.type())
|
.type(entry.type())
|
||||||
.atomicTextBlocks(atomicTextBlocks)
|
.atomicTextBlocks(atomicTextBlocks)
|
||||||
.pages(entry.node().getPages().stream().map(PageNode::getNumber).toArray(Long[]::new))
|
.pages(entry.node().getPages().stream().map(PageNode::getNumber).map(Integer::longValue).toArray(Long[]::new))
|
||||||
.numberOnPage(entry.node().getNumberOnPage())
|
.numberOnPage(entry.node().getNumberOnPage())
|
||||||
|
.properties(properties)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> buildImageProperties(ImageNode image) {
|
||||||
|
|
||||||
|
Map<String, String> properties = new HashMap<>();
|
||||||
|
properties.put("imageType", image.getImageType().toString());
|
||||||
|
properties.put("transparency", String.valueOf(image.isTransparency()));
|
||||||
|
properties.put("position", RectangleTransformations.toString(image.getPosition()));
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> buildTableCellProperties(TableCellNode tableCell) {
|
||||||
|
|
||||||
|
Map<String, String> properties = new HashMap<>();
|
||||||
|
properties.put("row", String.valueOf(tableCell.getRow()));
|
||||||
|
properties.put("col", String.valueOf(tableCell.getCol()));
|
||||||
|
properties.put("header", String.valueOf(tableCell.isHeader()));
|
||||||
|
|
||||||
|
if (tableCell.getPages().size() > 1 || tableCell.getBBox().keySet().size() > 1) {
|
||||||
|
throw new IllegalArgumentException("TableCell can only occur on a single page!");
|
||||||
|
}
|
||||||
|
String bBoxString = RectangleTransformations.toString(tableCell.getBBox().get(tableCell.getPages().stream().findFirst().get()));
|
||||||
|
properties.put("bBox", bBoxString);
|
||||||
|
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> buildTableProperties(TableNode table) {
|
||||||
|
|
||||||
|
Map<String, String> properties = new HashMap<>();
|
||||||
|
properties.put("numberOfRows", String.valueOf(table.getNumberOfRows()));
|
||||||
|
properties.put("numberOfCols", String.valueOf(table.getNumberOfCols()));
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private Long[] toAtomicTextBlockIds(TextBlock textBlock) {
|
private Long[] toAtomicTextBlockIds(TextBlock textBlock) {
|
||||||
|
|
||||||
return textBlock.getAtomicTextBlocks().stream().map(AtomicTextBlock::getId).toArray(Long[]::new);
|
return textBlock.getAtomicTextBlocks().stream().map(AtomicTextBlock::getId).toArray(Long[]::new);
|
||||||
@ -65,11 +116,7 @@ public class DocumentDataMapper {
|
|||||||
|
|
||||||
private PageData toPageData(PageNode pageNode) {
|
private PageData toPageData(PageNode pageNode) {
|
||||||
|
|
||||||
return PageData.builder()
|
return PageData.builder().height(pageNode.getHeight()).width(pageNode.getWidth()).number(pageNode.getNumber()).build();
|
||||||
.height(pageNode.getHeight())
|
|
||||||
.width(pageNode.getWidth())
|
|
||||||
.number(pageNode.getNumber())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -77,6 +124,7 @@ public class DocumentDataMapper {
|
|||||||
|
|
||||||
return AtomicTextBlockData.builder()
|
return AtomicTextBlockData.builder()
|
||||||
.id(atomicTextBlock.getId())
|
.id(atomicTextBlock.getId())
|
||||||
|
.page(atomicTextBlock.getPage().getNumber().longValue())
|
||||||
.searchText(atomicTextBlock.getSearchText())
|
.searchText(atomicTextBlock.getSearchText())
|
||||||
.start(atomicTextBlock.getBoundary().start())
|
.start(atomicTextBlock.getBoundary().start())
|
||||||
.end(atomicTextBlock.getBoundary().end())
|
.end(atomicTextBlock.getBoundary().end())
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
package com.iqser.red.service.redaction.v1.server.document.data.mapper;
|
package com.iqser.red.service.redaction.v1.server.document.data.mapper;
|
||||||
|
|
||||||
|
import static com.iqser.red.service.redaction.v1.server.document.graph.factory.RectangleTransformations.parseRectangle2D;
|
||||||
import static java.lang.Math.toIntExact;
|
import static java.lang.Math.toIntExact;
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
|
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.NotImplementedException;
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -23,15 +22,22 @@ import com.iqser.red.service.redaction.v1.server.document.data.TableOfContentsDa
|
|||||||
import com.iqser.red.service.redaction.v1.server.document.graph.Boundary;
|
import com.iqser.red.service.redaction.v1.server.document.graph.Boundary;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.DocumentGraph;
|
import com.iqser.red.service.redaction.v1.server.document.graph.DocumentGraph;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.TableOfContents;
|
import com.iqser.red.service.redaction.v1.server.document.graph.TableOfContents;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.document.graph.entity.ImageNode;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.FooterNode;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.HeaderNode;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.HeadlineNode;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.NodeType;
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.NodeType;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.PageNode;
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.PageNode;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.ParagraphNode;
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.ParagraphNode;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.SectionNode;
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.SectionNode;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.SemanticNode;
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.SemanticNode;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.TableCellNode;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.TableNode;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.textblock.AtomicTextBlock;
|
import com.iqser.red.service.redaction.v1.server.document.graph.textblock.AtomicTextBlock;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.textblock.TextBlock;
|
import com.iqser.red.service.redaction.v1.server.document.graph.textblock.TextBlock;
|
||||||
import com.iqser.red.service.redaction.v1.server.document.graph.textblock.TextBlockCollector;
|
import com.iqser.red.service.redaction.v1.server.document.graph.textblock.TextBlockCollector;
|
||||||
import com.iqser.red.service.redaction.v1.server.exception.NotFoundException;
|
import com.iqser.red.service.redaction.v1.server.exception.NotFoundException;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.redaction.model.ImageType;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class DocumentGraphMapper {
|
public class DocumentGraphMapper {
|
||||||
@ -42,7 +48,7 @@ public class DocumentGraphMapper {
|
|||||||
|
|
||||||
context.pages.addAll(documentData.getPages().stream().map(this::buildPage).toList());
|
context.pages.addAll(documentData.getPages().stream().map(this::buildPage).toList());
|
||||||
|
|
||||||
buildNodesFromTableOfContents(Collections.emptyList(), context);
|
context.tableOfContents.setEntries(buildEntries(documentData.getTableOfContents().getEntries(), context));
|
||||||
|
|
||||||
DocumentGraph documentGraph = DocumentGraph.builder()
|
DocumentGraph documentGraph = DocumentGraph.builder()
|
||||||
.numberOfPages(documentData.getPages().size())
|
.numberOfPages(documentData.getPages().size())
|
||||||
@ -54,67 +60,127 @@ public class DocumentGraphMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void buildNodesFromTableOfContents(List<Integer> currentTocId, Context context) {
|
private List<TableOfContents.Entry> buildEntries(List<TableOfContentsData.EntryData> entries, Context context) {
|
||||||
|
|
||||||
List<TableOfContentsData.EntryData> entries;
|
List<TableOfContents.Entry> newEntries = new LinkedList<>();
|
||||||
if (currentTocId.size() == 0) {
|
|
||||||
entries = context.documentData().getTableOfContents().getEntries();
|
|
||||||
} else {
|
|
||||||
entries = context.documentData().getTableOfContents().get(currentTocId).subEntries();
|
|
||||||
}
|
|
||||||
for (TableOfContentsData.EntryData entryData : entries) {
|
for (TableOfContentsData.EntryData entryData : entries) {
|
||||||
|
|
||||||
switch (entryData.type()) {
|
boolean terminal = isTerminal(entryData);
|
||||||
case SECTION -> buildSection(entryData, currentTocId, context);
|
List<PageNode> pages = Arrays.stream(entryData.pages()).map(pageNumber -> getPage(pageNumber, context)).toList();
|
||||||
case PARAGRAPH -> buildParagraph(entryData, currentTocId, context);
|
|
||||||
|
SemanticNode node = switch (entryData.type()) {
|
||||||
|
case SECTION -> buildSection(context);
|
||||||
|
case PARAGRAPH -> buildParagraph(context, terminal);
|
||||||
|
case HEADLINE -> buildHeadline(context, terminal);
|
||||||
|
case HEADER -> buildHeader(context, terminal);
|
||||||
|
case FOOTER -> buildFooter(context, terminal);
|
||||||
|
case TABLE -> buildTable(context, entryData.properties());
|
||||||
|
case TABLE_CELL -> buildTableCell(context, entryData.properties(), terminal);
|
||||||
|
case IMAGE -> buildImage(context, entryData.properties());
|
||||||
default -> throw new NotImplementedException("Not yet implemented for type " + entryData.type());
|
default -> throw new NotImplementedException("Not yet implemented for type " + entryData.type());
|
||||||
|
};
|
||||||
|
|
||||||
|
if (node.isTerminal()) {
|
||||||
|
TextBlock textBlock = toTextBlock(entryData.atomicTextBlocks(), context, node);
|
||||||
|
node.setTerminalTextBlock(textBlock);
|
||||||
}
|
}
|
||||||
|
List<Integer> tocId = Arrays.stream(entryData.tocId()).boxed().toList();
|
||||||
|
node.setTocId(tocId);
|
||||||
|
|
||||||
|
if (entryData.type() == NodeType.HEADER) {
|
||||||
|
pages.forEach(page -> page.setHeader((HeaderNode) node));
|
||||||
|
} else if (entryData.type() == NodeType.FOOTER) {
|
||||||
|
pages.forEach(page -> page.setFooter((FooterNode) node));
|
||||||
|
} else {
|
||||||
|
pages.forEach(page -> page.getMainBody().add(node));
|
||||||
|
}
|
||||||
|
newEntries.add(TableOfContents.Entry.builder().tocId(tocId).type(entryData.type()).children(buildEntries(entryData.subEntries(), context)).node(node).build());
|
||||||
}
|
}
|
||||||
|
return newEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void buildSection(TableOfContentsData.EntryData entryData, List<Integer> currentTocId, Context context) {
|
private HeadlineNode buildHeadline(Context context, boolean terminal) {
|
||||||
|
|
||||||
SectionNode section = SectionNode.builder().entities(new HashSet<>()).tableOfContents(context.tableOfContents()).build();
|
return HeadlineNode.builder().terminal(terminal).tableOfContents(context.tableOfContents()).build();
|
||||||
|
|
||||||
context.sections().add(section);
|
|
||||||
|
|
||||||
List<PageNode> pages = Arrays.stream(entryData.pages()).map(pageNumber -> getPage(pageNumber, context)).toList();
|
|
||||||
pages.forEach(page -> page.getMainBody().add(section));
|
|
||||||
section.getPages().addAll(pages);
|
|
||||||
|
|
||||||
List<Integer> sectionId = context.tableOfContents.createNewChildEntryAndReturnId(currentTocId, NodeType.SECTION, section);
|
|
||||||
section.setTocId(sectionId);
|
|
||||||
buildNodesFromTableOfContents(sectionId, context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void buildParagraph(TableOfContentsData.EntryData entryData, List<Integer> currentTocId, Context context) {
|
private static boolean isTerminal(TableOfContentsData.EntryData entryData) {
|
||||||
|
|
||||||
Set<PageNode> pages = Arrays.stream(entryData.pages()).map(pageNumber -> getPage(pageNumber, context)).collect(Collectors.toSet());
|
return entryData.atomicTextBlocks().length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
SectionNode parentSection = (SectionNode) context.tableOfContents().getEntryById(currentTocId).node();
|
|
||||||
ParagraphNode paragraph = ParagraphNode.builder().build();
|
|
||||||
TextBlock textBlock = toTextBlock(entryData.atomicTextBlocks(), context, paragraph);
|
|
||||||
|
|
||||||
paragraph.setTerminalTextBlock(textBlock);
|
private ImageNode buildImage(Context context, Map<String, String> properties) {
|
||||||
|
|
||||||
pages.forEach(page -> page.getMainBody().add(paragraph));
|
ImageType imageType = parseImageType(properties.get("imageType"));
|
||||||
for (PageNode page : pages) {
|
boolean transparency = Boolean.parseBoolean(properties.get("transparency"));
|
||||||
if (!parentSection.getPages().contains(page)) {
|
Rectangle2D position = parseRectangle2D(properties.get("position"));
|
||||||
parentSection.getPages().add(page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Integer> tocId = context.tableOfContents.createNewChildEntryAndReturnId(currentTocId, NodeType.PARAGRAPH, paragraph);
|
return ImageNode.builder().tableOfContents(context.tableOfContents()).imageType(imageType).transparency(transparency).position(position).build();
|
||||||
paragraph.setTocId(tocId);
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private TableCellNode buildTableCell(Context context, Map<String, String> properties, boolean terminal) {
|
||||||
|
|
||||||
|
int row = Integer.parseInt(properties.get("row"));
|
||||||
|
int col = Integer.parseInt(properties.get("col"));
|
||||||
|
boolean header = Boolean.parseBoolean(properties.get("header"));
|
||||||
|
Rectangle2D bBox = parseRectangle2D(properties.get("bBox"));
|
||||||
|
return TableCellNode.builder().terminal(terminal).bBox(bBox).header(header).col(col).row(row).tableOfContents(context.tableOfContents()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private TableNode buildTable(Context context, Map<String, String> properties) {
|
||||||
|
|
||||||
|
int numberOfRows = Integer.parseInt(properties.get("numberOfRows"));
|
||||||
|
int numberOfCols = Integer.parseInt(properties.get("numberOfCols"));
|
||||||
|
return TableNode.builder().tableOfContents(context.tableOfContents()).numberOfRows(numberOfRows).numberOfCols(numberOfCols).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private FooterNode buildFooter(Context context, boolean terminal) {
|
||||||
|
|
||||||
|
return FooterNode.builder().terminal(terminal).tableOfContents(context.tableOfContents()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private HeaderNode buildHeader(Context context, boolean terminal) {
|
||||||
|
|
||||||
|
return HeaderNode.builder().terminal(terminal).tableOfContents(context.tableOfContents()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private SectionNode buildSection(Context context) {
|
||||||
|
|
||||||
|
return SectionNode.builder().tableOfContents(context.tableOfContents()).build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ParagraphNode buildParagraph(Context context, boolean terminal) {
|
||||||
|
|
||||||
|
return ParagraphNode.builder().terminal(terminal).tableOfContents(context.tableOfContents()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static ImageType parseImageType(String imageType) {
|
||||||
|
|
||||||
|
return switch (imageType) {
|
||||||
|
case "LOGO" -> ImageType.LOGO;
|
||||||
|
case "FORMULA" -> ImageType.FORMULA;
|
||||||
|
case "SIGNATURE" -> ImageType.SIGNATURE;
|
||||||
|
case "OCR" -> ImageType.OCR;
|
||||||
|
default -> ImageType.OTHER;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private TextBlock toTextBlock(Long[] atomicTextBlockIds, Context context, SemanticNode parent) {
|
private TextBlock toTextBlock(Long[] atomicTextBlockIds, Context context, SemanticNode parent) {
|
||||||
|
|
||||||
return Arrays.stream(atomicTextBlockIds)
|
return Arrays.stream(atomicTextBlockIds)
|
||||||
.map(atomicTextBlockId -> toAtomicTextBlock(context.atomicTextBlockData.get(toIntExact(atomicTextBlockId)), parent))
|
.map(atomicTextBlockId -> toAtomicTextBlock(context.atomicTextBlockData.get(toIntExact(atomicTextBlockId)), parent, context))
|
||||||
.collect(new TextBlockCollector());
|
.collect(new TextBlockCollector());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,10 +191,11 @@ public class DocumentGraphMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private AtomicTextBlock toAtomicTextBlock(AtomicTextBlockData atomicTextBlockData, SemanticNode parent) {
|
private AtomicTextBlock toAtomicTextBlock(AtomicTextBlockData atomicTextBlockData, SemanticNode parent, Context context) {
|
||||||
|
|
||||||
return AtomicTextBlock.builder()
|
return AtomicTextBlock.builder()
|
||||||
.id(atomicTextBlockData.getId())
|
.id(atomicTextBlockData.getId())
|
||||||
|
.page(getPage(atomicTextBlockData.getPage(), context))
|
||||||
.searchText(atomicTextBlockData.getSearchText())
|
.searchText(atomicTextBlockData.getSearchText())
|
||||||
.boundary(new Boundary(atomicTextBlockData.getStart(), atomicTextBlockData.getEnd()))
|
.boundary(new Boundary(atomicTextBlockData.getStart(), atomicTextBlockData.getEnd()))
|
||||||
.lineBreaks(Ints.asList(atomicTextBlockData.getLineBreaks()))
|
.lineBreaks(Ints.asList(atomicTextBlockData.getLineBreaks()))
|
||||||
|
|||||||
@ -0,0 +1,2 @@
|
|||||||
|
package com.iqser.red.service.redaction.v1.server.document.data.mapper;public class PropertiesMapper {
|
||||||
|
}
|
||||||
@ -1,7 +1,10 @@
|
|||||||
package com.iqser.red.service.redaction.v1.server.document.graph.factory;
|
package com.iqser.red.service.redaction.v1.server.document.graph.factory;
|
||||||
|
|
||||||
|
import static java.lang.String.format;
|
||||||
|
|
||||||
import java.awt.geom.Area;
|
import java.awt.geom.Area;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
@ -60,6 +63,19 @@ public class RectangleTransformations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String toString(Rectangle2D rectangle2D) {
|
||||||
|
|
||||||
|
return format("%f,%f,%f,%f", rectangle2D.getX(), rectangle2D.getY(), rectangle2D.getWidth(), rectangle2D.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Rectangle2D parseRectangle2D(String bBox) {
|
||||||
|
|
||||||
|
List<Float> floats = Arrays.stream(bBox.split(",")).map(Float::parseFloat).toList();
|
||||||
|
return new Rectangle2D.Float(floats.get(0), floats.get(1), floats.get(2), floats.get(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class Rectangle2DUnion implements Collector<Rectangle2D, Area, Rectangle2D> {
|
private static class Rectangle2DUnion implements Collector<Rectangle2D, Area, Rectangle2D> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -21,7 +21,6 @@ public class TextBlockFactory {
|
|||||||
|
|
||||||
stringOffset = new AtomicInteger();
|
stringOffset = new AtomicInteger();
|
||||||
textBlockIdx = new AtomicLong();
|
textBlockIdx = new AtomicLong();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -64,7 +63,7 @@ public class TextBlockFactory {
|
|||||||
public AtomicTextBlock emptyTextBlock(SemanticNode parent, Integer numberOnPage, PageNode page) {
|
public AtomicTextBlock emptyTextBlock(SemanticNode parent, Integer numberOnPage, PageNode page) {
|
||||||
|
|
||||||
return AtomicTextBlock.builder()
|
return AtomicTextBlock.builder()
|
||||||
.id(textBlockIdx.get())
|
.id(textBlockIdx.getAndIncrement())
|
||||||
.boundary(new Boundary(stringOffset.get(), stringOffset.get()))
|
.boundary(new Boundary(stringOffset.get(), stringOffset.get()))
|
||||||
.searchText("")
|
.searchText("")
|
||||||
.lineBreaks(Collections.emptyList())
|
.lineBreaks(Collections.emptyList())
|
||||||
|
|||||||
@ -126,6 +126,12 @@ public interface SemanticNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default void setTerminalTextBlock(TextBlock textBlock) {
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Each AtomicTextBlock has an index on its page, this returns the number of the first AtomicTextBlock underneath this node.
|
* Each AtomicTextBlock has an index on its page, this returns the number of the first AtomicTextBlock underneath this node.
|
||||||
* If this node does not have any AtomicTexBlocks underneath it, e.g. an empty TableCell. It returns -1.
|
* If this node does not have any AtomicTexBlocks underneath it, e.g. an empty TableCell. It returns -1.
|
||||||
|
|||||||
@ -48,7 +48,7 @@ public class ConcatenatedTextBlock implements TextBlock {
|
|||||||
boundary.setStart(textBlock.getBoundary().start());
|
boundary.setStart(textBlock.getBoundary().start());
|
||||||
boundary.setEnd(textBlock.getBoundary().end());
|
boundary.setEnd(textBlock.getBoundary().end());
|
||||||
} else if (boundary.end() != textBlock.getBoundary().start()) {
|
} else if (boundary.end() != textBlock.getBoundary().start()) {
|
||||||
throw new UnsupportedOperationException(format("Can only concat consecutive TextBlocks, trying to concat %s to %s", textBlock.getBoundary(), boundary));
|
throw new UnsupportedOperationException(format("Can only concat consecutive TextBlocks, trying to concat %s and %s", boundary, textBlock.getBoundary()));
|
||||||
}
|
}
|
||||||
this.atomicTextBlocks.addAll(textBlock.getAtomicTextBlocks());
|
this.atomicTextBlocks.addAll(textBlock.getAtomicTextBlocks());
|
||||||
boundary.setEnd(textBlock.getBoundary().end());
|
boundary.setEnd(textBlock.getBoundary().end());
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user