RED-6009 - Document Tree Structure
* renamed terminal to leaf
This commit is contained in:
parent
e31a7b1a2f
commit
c151b4a94b
@ -60,8 +60,8 @@ public class DocumentDataMapper {
|
||||
|
||||
Long[] atomicTextBlocks;
|
||||
|
||||
if (entry.getNode().isTerminal()) {
|
||||
atomicTextBlocks = toAtomicTextBlockIds(entry.getNode().getTerminalTextBlock());
|
||||
if (entry.getNode().isLeaf()) {
|
||||
atomicTextBlocks = toAtomicTextBlockIds(entry.getNode().getLeafTextBlock());
|
||||
} else {
|
||||
atomicTextBlocks = new Long[]{};
|
||||
}
|
||||
|
||||
@ -82,9 +82,9 @@ public class DocumentGraphMapper {
|
||||
default -> throw new UnsupportedOperationException("Not yet implemented for type " + entryData.getType());
|
||||
};
|
||||
|
||||
if (node.isTerminal()) {
|
||||
if (node.isLeaf()) {
|
||||
TextBlock textBlock = toTextBlock(entryData.getAtomicBlocks(), context, node);
|
||||
node.setTerminalTextBlock(textBlock);
|
||||
node.setLeafTextBlock(textBlock);
|
||||
}
|
||||
List<Integer> tocId = Arrays.stream(entryData.getTreeId()).boxed().toList();
|
||||
node.setTreeId(tocId);
|
||||
@ -104,7 +104,7 @@ public class DocumentGraphMapper {
|
||||
|
||||
private Headline buildHeadline(Context context, boolean terminal) {
|
||||
|
||||
return Headline.builder().terminal(terminal).documentTree(context.documentTree()).build();
|
||||
return Headline.builder().leaf(terminal).documentTree(context.documentTree()).build();
|
||||
}
|
||||
|
||||
|
||||
@ -128,7 +128,7 @@ public class DocumentGraphMapper {
|
||||
|
||||
TableCell.TableCellBuilder builder = TableCell.builder();
|
||||
PropertiesMapper.parseTableCellProperties(properties, builder);
|
||||
return builder.terminal(terminal).documentTree(context.documentTree()).build();
|
||||
return builder.leaf(terminal).documentTree(context.documentTree()).build();
|
||||
}
|
||||
|
||||
|
||||
@ -142,13 +142,13 @@ public class DocumentGraphMapper {
|
||||
|
||||
private Footer buildFooter(Context context, boolean terminal) {
|
||||
|
||||
return Footer.builder().terminal(terminal).documentTree(context.documentTree()).build();
|
||||
return Footer.builder().leaf(terminal).documentTree(context.documentTree()).build();
|
||||
}
|
||||
|
||||
|
||||
private Header buildHeader(Context context, boolean terminal) {
|
||||
|
||||
return Header.builder().terminal(terminal).documentTree(context.documentTree()).build();
|
||||
return Header.builder().leaf(terminal).documentTree(context.documentTree()).build();
|
||||
}
|
||||
|
||||
|
||||
@ -162,7 +162,7 @@ public class DocumentGraphMapper {
|
||||
|
||||
private Paragraph buildParagraph(Context context, boolean terminal) {
|
||||
|
||||
return Paragraph.builder().terminal(terminal).documentTree(context.documentTree()).build();
|
||||
return Paragraph.builder().leaf(terminal).documentTree(context.documentTree()).build();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -94,12 +94,12 @@ public class DocumentGraphFactory {
|
||||
|
||||
if (node instanceof Headline headline) {
|
||||
List<Integer> tocId = context.documentTree.createNewChildEntryAndReturnId(parentNode.getTreeId(), NodeType.HEADLINE, node);
|
||||
headline.setTerminalTextBlock(textBlock);
|
||||
headline.setLeafTextBlock(textBlock);
|
||||
headline.setTreeId(tocId);
|
||||
}
|
||||
if (node instanceof Paragraph paragraph) {
|
||||
List<Integer> tocId = context.documentTree.createNewChildEntryAndReturnId(parentNode.getTreeId(), NodeType.PARAGRAPH, node);
|
||||
paragraph.setTerminalTextBlock(textBlock);
|
||||
paragraph.setLeafTextBlock(textBlock);
|
||||
paragraph.setTreeId(tocId);
|
||||
}
|
||||
}
|
||||
@ -166,7 +166,7 @@ public class DocumentGraphFactory {
|
||||
page);
|
||||
List<Integer> tocId = context.getDocumentTree().createNewMainEntryAndReturnId(NodeType.FOOTER, footer);
|
||||
footer.setTreeId(tocId);
|
||||
footer.setTerminalTextBlock(textBlock);
|
||||
footer.setLeafTextBlock(textBlock);
|
||||
page.setFooter(footer);
|
||||
}
|
||||
|
||||
@ -182,7 +182,7 @@ public class DocumentGraphFactory {
|
||||
page);
|
||||
List<Integer> tocId = context.getDocumentTree().createNewMainEntryAndReturnId(NodeType.HEADER, header);
|
||||
header.setTreeId(tocId);
|
||||
header.setTerminalTextBlock(textBlock);
|
||||
header.setLeafTextBlock(textBlock);
|
||||
page.setHeader(header);
|
||||
}
|
||||
|
||||
@ -194,7 +194,7 @@ public class DocumentGraphFactory {
|
||||
AtomicTextBlock textBlock = context.textBlockFactory.emptyTextBlock(footer, context, page);
|
||||
List<Integer> tocId = context.getDocumentTree().createNewMainEntryAndReturnId(NodeType.FOOTER, footer);
|
||||
footer.setTreeId(tocId);
|
||||
footer.setTerminalTextBlock(textBlock);
|
||||
footer.setLeafTextBlock(textBlock);
|
||||
page.setFooter(footer);
|
||||
}
|
||||
|
||||
@ -206,7 +206,7 @@ public class DocumentGraphFactory {
|
||||
AtomicTextBlock textBlock = context.textBlockFactory.emptyTextBlock(header, 0, page);
|
||||
List<Integer> tocId = context.getDocumentTree().createNewMainEntryAndReturnId(NodeType.HEADER, header);
|
||||
header.setTreeId(tocId);
|
||||
header.setTerminalTextBlock(textBlock);
|
||||
header.setLeafTextBlock(textBlock);
|
||||
page.setHeader(header);
|
||||
}
|
||||
|
||||
|
||||
@ -95,27 +95,27 @@ public class TableNodeFactory {
|
||||
tableCell.setTreeId(tocId);
|
||||
|
||||
if (cell.getTextBlocks().isEmpty()) {
|
||||
tableCell.setTerminalTextBlock(context.getTextBlockFactory().emptyTextBlock(parentNode, context, page));
|
||||
tableCell.setTerminal(true);
|
||||
tableCell.setLeafTextBlock(context.getTextBlockFactory().emptyTextBlock(parentNode, context, page));
|
||||
tableCell.setLeaf(true);
|
||||
|
||||
} else if (cell.getTextBlocks().size() == 1) {
|
||||
textBlock = context.getTextBlockFactory().buildAtomicTextBlock(cell.getTextBlocks().get(0).getSequences(), tableCell, context, page);
|
||||
tableCell.setTerminalTextBlock(textBlock);
|
||||
tableCell.setTerminal(true);
|
||||
tableCell.setLeafTextBlock(textBlock);
|
||||
tableCell.setLeaf(true);
|
||||
|
||||
} else if (firstTextBlockIsHeadline(cell)) {
|
||||
SectionNodeFactory.addSection(tableCell, cell.getTextBlocks().stream().map(tb -> (AbstractPageBlock) tb).toList(), emptyList(), context);
|
||||
tableCell.setTerminal(false);
|
||||
tableCell.setLeaf(false);
|
||||
|
||||
} else if (cellAreaIsSmallerThanPageAreaTimesThreshold(cell, page)) {
|
||||
List<TextPositionSequence> sequences = TextPositionOperations.mergeAndSortTextPositionSequenceByYThenX(cell.getTextBlocks());
|
||||
textBlock = context.getTextBlockFactory().buildAtomicTextBlock(sequences, tableCell, context, page);
|
||||
tableCell.setTerminalTextBlock(textBlock);
|
||||
tableCell.setTerminal(true);
|
||||
tableCell.setLeafTextBlock(textBlock);
|
||||
tableCell.setLeaf(true);
|
||||
|
||||
} else {
|
||||
cell.getTextBlocks().forEach(tb -> DocumentGraphFactory.addParagraphOrHeadline(tableCell, tb, context, emptyList()));
|
||||
tableCell.setTerminal(false);
|
||||
tableCell.setLeaf(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ public class DocumentTree {
|
||||
|
||||
public TextBlock buildTextBlock() {
|
||||
|
||||
return streamAllEntriesInOrder().map(Entry::getNode).filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock).collect(new TextBlockCollector());
|
||||
return streamAllEntriesInOrder().map(Entry::getNode).filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ public class Document implements SemanticNode {
|
||||
|
||||
public Stream<TextBlock> streamTerminalTextBlocksInOrder() {
|
||||
|
||||
return streamAllNodes().filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock);
|
||||
return streamAllNodes().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -24,10 +24,10 @@ import lombok.experimental.FieldDefaults;
|
||||
public class Footer implements SemanticNode {
|
||||
|
||||
List<Integer> treeId;
|
||||
TextBlock terminalTextBlock;
|
||||
TextBlock leafTextBlock;
|
||||
|
||||
@Builder.Default
|
||||
boolean terminal = true;
|
||||
boolean leaf = true;
|
||||
|
||||
@EqualsAndHashCode.Exclude
|
||||
DocumentTree documentTree;
|
||||
@ -40,14 +40,14 @@ public class Footer implements SemanticNode {
|
||||
@Override
|
||||
public TextBlock buildTextBlock() {
|
||||
|
||||
return terminalTextBlock;
|
||||
return leafTextBlock;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return treeId + ": " + NodeType.FOOTER + ": " + terminalTextBlock.buildSummary();
|
||||
return treeId + ": " + NodeType.FOOTER + ": " + leafTextBlock.buildSummary();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -24,10 +24,10 @@ import lombok.experimental.FieldDefaults;
|
||||
public class Header implements SemanticNode {
|
||||
|
||||
List<Integer> treeId;
|
||||
TextBlock terminalTextBlock;
|
||||
TextBlock leafTextBlock;
|
||||
|
||||
@Builder.Default
|
||||
boolean terminal = true;
|
||||
boolean leaf = true;
|
||||
|
||||
@EqualsAndHashCode.Exclude
|
||||
DocumentTree documentTree;
|
||||
@ -40,14 +40,14 @@ public class Header implements SemanticNode {
|
||||
@Override
|
||||
public TextBlock buildTextBlock() {
|
||||
|
||||
return terminalTextBlock;
|
||||
return leafTextBlock;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return treeId + ": " + NodeType.HEADER + ": " + terminalTextBlock.buildSummary();
|
||||
return treeId + ": " + NodeType.HEADER + ": " + leafTextBlock.buildSummary();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -24,10 +24,10 @@ import lombok.experimental.FieldDefaults;
|
||||
public class Headline implements SemanticNode {
|
||||
|
||||
List<Integer> treeId;
|
||||
TextBlock terminalTextBlock;
|
||||
TextBlock leafTextBlock;
|
||||
|
||||
@Builder.Default
|
||||
boolean terminal = true;
|
||||
boolean leaf = true;
|
||||
|
||||
@EqualsAndHashCode.Exclude
|
||||
DocumentTree documentTree;
|
||||
@ -40,14 +40,14 @@ public class Headline implements SemanticNode {
|
||||
@Override
|
||||
public TextBlock buildTextBlock() {
|
||||
|
||||
return terminalTextBlock;
|
||||
return leafTextBlock;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return treeId + ": " + NodeType.HEADLINE + ": " + terminalTextBlock.buildSummary();
|
||||
return treeId + ": " + NodeType.HEADLINE + ": " + leafTextBlock.buildSummary();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@ public class Image implements SemanticNode {
|
||||
@Override
|
||||
public TextBlock buildTextBlock() {
|
||||
|
||||
return streamAllSubNodes().filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock).collect(new TextBlockCollector());
|
||||
return streamAllSubNodes().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@ public class Page {
|
||||
|
||||
public TextBlock getMainBodyTextBlock() {
|
||||
|
||||
return mainBody.stream().filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock).collect(new TextBlockCollector());
|
||||
return mainBody.stream().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -22,10 +22,10 @@ import lombok.experimental.FieldDefaults;
|
||||
public class Paragraph implements SemanticNode {
|
||||
|
||||
List<Integer> treeId;
|
||||
TextBlock terminalTextBlock;
|
||||
TextBlock leafTextBlock;
|
||||
|
||||
@Builder.Default
|
||||
boolean terminal = true;
|
||||
boolean leaf = true;
|
||||
|
||||
@EqualsAndHashCode.Exclude
|
||||
DocumentTree documentTree;
|
||||
@ -38,14 +38,14 @@ public class Paragraph implements SemanticNode {
|
||||
@Override
|
||||
public TextBlock buildTextBlock() {
|
||||
|
||||
return terminalTextBlock;
|
||||
return leafTextBlock;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return treeId + ": " + NodeType.PARAGRAPH + ": " + terminalTextBlock.buildSummary();
|
||||
return treeId + ": " + NodeType.PARAGRAPH + ": " + leafTextBlock.buildSummary();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ public class Section implements SemanticNode {
|
||||
public TextBlock buildTextBlock() {
|
||||
|
||||
if (textBlock == null) {
|
||||
textBlock = streamAllSubNodes().filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock).collect(new TextBlockCollector());
|
||||
textBlock = streamAllSubNodes().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
|
||||
}
|
||||
return textBlock;
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ public interface SemanticNode {
|
||||
/**
|
||||
* Searches all Nodes located underneath this Node in the DocumentTree and concatenates their AtomicTextBlocks into a single TextBlockEntity.
|
||||
* So, for a Section all TextBlocks of Subsections, Paragraphs, and Tables are concatenated into a single TextBlockEntity
|
||||
* If the Node is Terminal, the TerminalTextBlock will be returned instead.
|
||||
* If the Node is a Leaf, the LeafTextBlock will be returned instead.
|
||||
*
|
||||
* @return TextBlock containing all AtomicTextBlocks that are located under this Node.
|
||||
*/
|
||||
@ -131,36 +131,36 @@ public interface SemanticNode {
|
||||
|
||||
|
||||
/**
|
||||
* Terminal means a SemanticNode has direct access to a TextBlock, by default this is false and must be overridden.
|
||||
* Currently only Sections, Images, and Tables are not terminal.
|
||||
* A TableCell might be Terminal depending on its area compared to the page.
|
||||
* Leaf means a SemanticNode has direct access to a TextBlock, by default this is false and must be overridden.
|
||||
* Currently only Sections, Images, and Tables are not leaves.
|
||||
* A TableCell might be a leaf depending on its area compared to the page.
|
||||
*
|
||||
* @return boolean, indicating if a Node has direct access to a TextBlock
|
||||
*/
|
||||
default boolean isTerminal() {
|
||||
default boolean isLeaf() {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Terminal means a SemanticNode has direct access to a TextBlock, by default this is false and must be overridden.
|
||||
* Currently only Sections and Tables are not terminal.
|
||||
* Leaf means a SemanticNode has direct access to a TextBlock, by default this is false and must be overridden.
|
||||
* Currently only Sections and Tables are no leaves.
|
||||
*
|
||||
* @return AtomicTextBlock
|
||||
*/
|
||||
default TextBlock getTerminalTextBlock() {
|
||||
default TextBlock getLeafTextBlock() {
|
||||
|
||||
throw new UnsupportedOperationException("Only terminal Nodes have access to TerminalTextBlocks!");
|
||||
throw new UnsupportedOperationException("Only leaf Nodes have access to LeafTextBlocks!");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Should only be used during construction of the Graph. Sets the TerminalTextBlock of this SemanticNode.
|
||||
* Should only be used during construction of the Graph. Sets the LeafTextBlock of this SemanticNode.
|
||||
*
|
||||
* @param textBlock the TextBlock to set as the terminalTextBlock of this SemanticNode
|
||||
* @param textBlock the TextBlock to set as the LeafTextBlock of this SemanticNode
|
||||
*/
|
||||
default void setTerminalTextBlock(TextBlock textBlock) {
|
||||
default void setLeafTextBlock(TextBlock textBlock) {
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@ -345,7 +345,7 @@ public interface SemanticNode {
|
||||
|
||||
|
||||
/**
|
||||
* If this Node is Terminal it will calculate the boundingBox of its TerminalTextBlock, otherwise it will calculate the Union of the BoundingBoxes of all its Children.
|
||||
* If this Node is a Leaf it will calculate the boundingBox of its LeafTextBlock, otherwise it will calculate the Union of the BoundingBoxes of all its Children.
|
||||
* If called on the Document, it will return the cropbox of each page
|
||||
*
|
||||
* @return Rectangle2D fully encapsulating this Node for each page.
|
||||
@ -353,8 +353,8 @@ public interface SemanticNode {
|
||||
default Map<Page, Rectangle2D> getBBox() {
|
||||
|
||||
Map<Page, Rectangle2D> bBoxPerPage = new HashMap<>();
|
||||
if (isTerminal()) {
|
||||
return getBBoxFromTerminalTextBlock(bBoxPerPage);
|
||||
if (isLeaf()) {
|
||||
return getBBoxFromLeafTextBlock(bBoxPerPage);
|
||||
}
|
||||
|
||||
return getBBoxFromChildren(bBoxPerPage);
|
||||
@ -397,7 +397,7 @@ public interface SemanticNode {
|
||||
* @param bBoxPerPage initial empty BoundingBox
|
||||
* @return The union of all BoundingBoxes of the TextBlock of this node
|
||||
*/
|
||||
private Map<Page, Rectangle2D> getBBoxFromTerminalTextBlock(Map<Page, Rectangle2D> bBoxPerPage) {
|
||||
private Map<Page, Rectangle2D> getBBoxFromLeafTextBlock(Map<Page, Rectangle2D> bBoxPerPage) {
|
||||
|
||||
Map<Page, List<AtomicTextBlock>> atomicTextBlockPerPage = buildTextBlock().getAtomicTextBlocks().stream().collect(Collectors.groupingBy(AtomicTextBlock::getPage));
|
||||
atomicTextBlockPerPage.forEach((page, atbs) -> bBoxPerPage.put(page, RectangleTransformations.bBoxUnionAtomicTextBlock(atbs)));
|
||||
|
||||
@ -174,7 +174,7 @@ public class Table implements SemanticNode {
|
||||
public TextBlock buildTextBlock() {
|
||||
|
||||
if (textBlock == null) {
|
||||
textBlock = streamAllSubNodes().filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock).collect(new TextBlockCollector());
|
||||
textBlock = streamAllSubNodes().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
|
||||
}
|
||||
return textBlock;
|
||||
}
|
||||
|
||||
@ -34,8 +34,8 @@ public class TableCell implements SemanticNode {
|
||||
Rectangle2D bBox;
|
||||
|
||||
@Builder.Default
|
||||
boolean terminal = true;
|
||||
TextBlock terminalTextBlock;
|
||||
boolean leaf = true;
|
||||
TextBlock leafTextBlock;
|
||||
|
||||
TextBlock textBlock;
|
||||
|
||||
@ -59,12 +59,12 @@ public class TableCell implements SemanticNode {
|
||||
@Override
|
||||
public TextBlock buildTextBlock() {
|
||||
|
||||
if (terminal) {
|
||||
return terminalTextBlock;
|
||||
if (leaf) {
|
||||
return leafTextBlock;
|
||||
}
|
||||
|
||||
if (textBlock == null) {
|
||||
textBlock = streamAllSubNodes().filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock).collect(new TextBlockCollector());
|
||||
textBlock = streamAllSubNodes().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
|
||||
}
|
||||
return textBlock;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user