Compare commits
57
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25929cfe2f | ||
|
|
b447788fcb | ||
|
|
65671d6f25 | ||
|
|
3ef0d9eccd | ||
|
|
64141fc7a2 | ||
|
|
fcbc005102 | ||
|
|
2fdc53429c | ||
|
|
3b30732352 | ||
|
|
e01c0a8d3b | ||
|
|
5ef5d5509b | ||
|
|
ab70536d06 | ||
|
|
5e091402c7 | ||
|
|
03f5acd417 | ||
|
|
8ca41cf340 | ||
|
|
cee6c74d73 | ||
|
|
d8394d9a78 | ||
|
|
d3c4413ece | ||
|
|
d614aed96a | ||
|
|
63953ecf2d | ||
|
|
8c28a46817 | ||
|
|
072ad3bf23 | ||
|
|
8a11d838b9 | ||
|
|
ed37b4bedf | ||
|
|
dda5a2c719 | ||
|
|
0f641670f7 | ||
|
|
b08c102f76 | ||
|
|
6acc85266c | ||
|
|
a4d6d2326e | ||
|
|
a337fdf684 | ||
|
|
95e6fdecd7 | ||
|
|
1337c56591 | ||
|
|
31bf4ba8c8 | ||
|
|
f034c5bfa0 | ||
|
|
41ba531734 | ||
|
|
c392813402 | ||
|
|
4a624f9642 | ||
|
|
f6c60aa5eb | ||
|
|
90a1187921 | ||
|
|
09c18c110a | ||
|
|
9012162542 | ||
|
|
49604cd96e | ||
|
|
943a6b6536 | ||
|
|
302d8b884f | ||
|
|
a50b047cbb | ||
|
|
8de9d8309f | ||
|
|
3b12242355 | ||
|
|
e8605f4956 | ||
|
|
f4a5b5fcbf | ||
|
|
8496b48cde | ||
|
|
de266dcfe5 | ||
|
|
10e525f0de | ||
|
|
e0e5e35b30 | ||
|
|
e1d8d1ea3b | ||
|
|
1546c05dd8 | ||
|
|
7c88c30ca7 | ||
|
|
50427d08dc | ||
|
|
338c6c5dd0 |
@@ -21,6 +21,5 @@ deploy:
|
||||
dotenv: version.env
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
- if: $CI_COMMIT_BRANCH =~ /^feature/ && $CI_COMMIT_TAG == ""
|
||||
- if: $CI_COMMIT_BRANCH =~ /^release/
|
||||
- if: $CI_COMMIT_TAG
|
||||
|
||||
@@ -7,5 +7,4 @@ description = "layoutparser-service-internal-api"
|
||||
|
||||
dependencies {
|
||||
implementation("io.swagger.core.v3:swagger-annotations:2.2.15")
|
||||
implementation("com.google.protobuf:protobuf-java-util:4.27.1")
|
||||
}
|
||||
|
||||
+5
-20
@@ -2,11 +2,6 @@ package com.knecon.fforesight.service.layoutparser.internal.api.data.redaction;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPageProto.AllDocumentPages;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPositionDataProto.AllDocumentPositionData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructureProto.DocumentStructure;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentTextDataProto.AllDocumentTextData;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -18,26 +13,16 @@ import lombok.experimental.FieldDefaults;
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
||||
@Schema(description = "Object containing the complete document layout parsing information. It is split into 4 categories, structure, text, positions and pages: "
|
||||
+ "The document tree structure of SemanticNodes such as Section, Paragraph, Headline, etc. "
|
||||
+ "The text, which is stored as separate blocks of data. "
|
||||
+ "The text positions, which are also stored as separate blocks. The Blocks are equal to the text blocks in length and order. "
|
||||
+ "The page information.")
|
||||
@Schema(description = "Object containing the complete document layout parsing information. It is split into 4 categories, structure, text, positions and pages: " + "The document tree structure of SemanticNodes such as Section, Paragraph, Headline, etc. " + "The text, which is stored as separate blocks of data. " + "The text positions, which are also stored as separate blocks. The Blocks are equal to the text blocks in length and order. " + "The page information.")
|
||||
public class DocumentData implements Serializable {
|
||||
|
||||
@Schema(description = "Contains information about the document's pages.")
|
||||
AllDocumentPages documentPages;
|
||||
DocumentPage[] documentPages;
|
||||
@Schema(description = "Contains information about the document's text.")
|
||||
AllDocumentTextData documentTextData;
|
||||
DocumentTextData[] documentTextData;
|
||||
@Schema(description = "Contains information about the document's text positions.")
|
||||
AllDocumentPositionData documentPositions;
|
||||
DocumentPositionData[] documentPositions;
|
||||
@Schema(description = "Contains information about the document's semantic structure.")
|
||||
DocumentStructureWrapper documentStructureWrapper;
|
||||
|
||||
|
||||
public DocumentStructure getDocumentStructure() {
|
||||
|
||||
return documentStructureWrapper.getDocumentStructure();
|
||||
}
|
||||
DocumentStructure documentStructure;
|
||||
|
||||
}
|
||||
|
||||
-1
@@ -10,7 +10,6 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Deprecated
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
|
||||
-1816
File diff suppressed because it is too large
Load Diff
-1
@@ -10,7 +10,6 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Deprecated
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
|
||||
-3086
File diff suppressed because it is too large
Load Diff
-1
@@ -16,7 +16,6 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Deprecated
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
|
||||
-799
@@ -1,799 +0,0 @@
|
||||
package com.knecon.fforesight.service.layoutparser.internal.api.data.redaction;
|
||||
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// NO CHECKED-IN PROTOBUF GENCODE
|
||||
// source: DocumentStructure.proto
|
||||
// Protobuf Java Version: 4.27.1
|
||||
@SuppressWarnings("all")
|
||||
public final class DocumentStructureProto {
|
||||
|
||||
private DocumentStructureProto() {}
|
||||
|
||||
|
||||
static {
|
||||
com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
|
||||
/* major= */ 4,
|
||||
/* minor= */ 27,
|
||||
/* patch= */ 1,
|
||||
/* suffix= */ "", DocumentStructureProto.class.getName());
|
||||
}
|
||||
|
||||
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) {
|
||||
|
||||
registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry);
|
||||
}
|
||||
|
||||
|
||||
public interface DocumentStructureOrBuilder extends
|
||||
// @@protoc_insertion_point(interface_extends:DocumentStructure)
|
||||
com.google.protobuf.MessageOrBuilder {
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*
|
||||
* @return Whether the root field is set.
|
||||
*/
|
||||
boolean hasRoot();
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*
|
||||
* @return The root.
|
||||
*/
|
||||
EntryDataProto.EntryData getRoot();
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*/
|
||||
EntryDataProto.EntryDataOrBuilder getRootOrBuilder();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Protobuf type {@code DocumentStructure}
|
||||
*/
|
||||
public static final class DocumentStructure extends com.google.protobuf.GeneratedMessage implements
|
||||
// @@protoc_insertion_point(message_implements:DocumentStructure)
|
||||
DocumentStructureOrBuilder {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
static {
|
||||
com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
|
||||
/* major= */ 4,
|
||||
/* minor= */ 27,
|
||||
/* patch= */ 1,
|
||||
/* suffix= */ "", DocumentStructure.class.getName());
|
||||
}
|
||||
|
||||
// Use DocumentStructure.newBuilder() to construct.
|
||||
private DocumentStructure(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
|
||||
|
||||
super(builder);
|
||||
}
|
||||
|
||||
|
||||
private DocumentStructure() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
|
||||
|
||||
return DocumentStructureProto.internal_static_DocumentStructure_descriptor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected FieldAccessorTable internalGetFieldAccessorTable() {
|
||||
|
||||
return DocumentStructureProto.internal_static_DocumentStructure_fieldAccessorTable.ensureFieldAccessorsInitialized(DocumentStructure.class, Builder.class);
|
||||
}
|
||||
|
||||
|
||||
private int bitField0_;
|
||||
public static final int ROOT_FIELD_NUMBER = 1;
|
||||
private EntryDataProto.EntryData root_;
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*
|
||||
* @return Whether the root field is set.
|
||||
*/
|
||||
@Override
|
||||
public boolean hasRoot() {
|
||||
|
||||
return ((bitField0_ & 0x00000001) != 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*
|
||||
* @return The root.
|
||||
*/
|
||||
@Override
|
||||
public EntryDataProto.EntryData getRoot() {
|
||||
|
||||
return root_ == null ? EntryDataProto.EntryData.getDefaultInstance() : root_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*/
|
||||
@Override
|
||||
public EntryDataProto.EntryDataOrBuilder getRootOrBuilder() {
|
||||
|
||||
return root_ == null ? EntryDataProto.EntryData.getDefaultInstance() : root_;
|
||||
}
|
||||
|
||||
|
||||
private byte memoizedIsInitialized = -1;
|
||||
|
||||
|
||||
@Override
|
||||
public final boolean isInitialized() {
|
||||
|
||||
byte isInitialized = memoizedIsInitialized;
|
||||
if (isInitialized == 1) {
|
||||
return true;
|
||||
}
|
||||
if (isInitialized == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memoizedIsInitialized = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException {
|
||||
|
||||
if (((bitField0_ & 0x00000001) != 0)) {
|
||||
output.writeMessage(1, getRoot());
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getSerializedSize() {
|
||||
|
||||
int size = memoizedSize;
|
||||
if (size != -1) {
|
||||
return size;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
if (((bitField0_ & 0x00000001) != 0)) {
|
||||
size += com.google.protobuf.CodedOutputStream.computeMessageSize(1, getRoot());
|
||||
}
|
||||
size += getUnknownFields().getSerializedSize();
|
||||
memoizedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof DocumentStructure)) {
|
||||
return super.equals(obj);
|
||||
}
|
||||
DocumentStructure other = (DocumentStructure) obj;
|
||||
|
||||
if (hasRoot() != other.hasRoot()) {
|
||||
return false;
|
||||
}
|
||||
if (hasRoot()) {
|
||||
if (!getRoot().equals(other.getRoot())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!getUnknownFields().equals(other.getUnknownFields())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
if (memoizedHashCode != 0) {
|
||||
return memoizedHashCode;
|
||||
}
|
||||
int hash = 41;
|
||||
hash = (19 * hash) + getDescriptor().hashCode();
|
||||
if (hasRoot()) {
|
||||
hash = (37 * hash) + ROOT_FIELD_NUMBER;
|
||||
hash = (53 * hash) + getRoot().hashCode();
|
||||
}
|
||||
hash = (29 * hash) + getUnknownFields().hashCode();
|
||||
memoizedHashCode = hash;
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
public static DocumentStructure parseFrom(java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
|
||||
return PARSER.parseFrom(data);
|
||||
}
|
||||
|
||||
|
||||
public static DocumentStructure parseFrom(java.nio.ByteBuffer data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
|
||||
return PARSER.parseFrom(data, extensionRegistry);
|
||||
}
|
||||
|
||||
|
||||
public static DocumentStructure parseFrom(com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
|
||||
return PARSER.parseFrom(data);
|
||||
}
|
||||
|
||||
|
||||
public static DocumentStructure parseFrom(com.google.protobuf.ByteString data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
|
||||
return PARSER.parseFrom(data, extensionRegistry);
|
||||
}
|
||||
|
||||
|
||||
public static DocumentStructure parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
|
||||
return PARSER.parseFrom(data);
|
||||
}
|
||||
|
||||
|
||||
public static DocumentStructure parseFrom(byte[] data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
|
||||
return PARSER.parseFrom(data, extensionRegistry);
|
||||
}
|
||||
|
||||
|
||||
public static DocumentStructure parseFrom(java.io.InputStream input) throws java.io.IOException {
|
||||
|
||||
return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input);
|
||||
}
|
||||
|
||||
|
||||
public static DocumentStructure parseFrom(java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
|
||||
|
||||
return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input, extensionRegistry);
|
||||
}
|
||||
|
||||
|
||||
public static DocumentStructure parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException {
|
||||
|
||||
return com.google.protobuf.GeneratedMessage.parseDelimitedWithIOException(PARSER, input);
|
||||
}
|
||||
|
||||
|
||||
public static DocumentStructure parseDelimitedFrom(java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
|
||||
|
||||
return com.google.protobuf.GeneratedMessage.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
|
||||
}
|
||||
|
||||
|
||||
public static DocumentStructure parseFrom(com.google.protobuf.CodedInputStream input) throws java.io.IOException {
|
||||
|
||||
return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input);
|
||||
}
|
||||
|
||||
|
||||
public static DocumentStructure parseFrom(com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
|
||||
|
||||
return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input, extensionRegistry);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Builder newBuilderForType() {return newBuilder();}
|
||||
|
||||
|
||||
public static Builder newBuilder() {
|
||||
|
||||
return DEFAULT_INSTANCE.toBuilder();
|
||||
}
|
||||
|
||||
|
||||
public static Builder newBuilder(DocumentStructure prototype) {
|
||||
|
||||
return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Builder toBuilder() {
|
||||
|
||||
return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Builder newBuilderForType(BuilderParent parent) {
|
||||
|
||||
Builder builder = new Builder(parent);
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Protobuf type {@code DocumentStructure}
|
||||
*/
|
||||
public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder<Builder> implements
|
||||
// @@protoc_insertion_point(builder_implements:DocumentStructure)
|
||||
DocumentStructureOrBuilder {
|
||||
|
||||
public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
|
||||
|
||||
return DocumentStructureProto.internal_static_DocumentStructure_descriptor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected FieldAccessorTable internalGetFieldAccessorTable() {
|
||||
|
||||
return DocumentStructureProto.internal_static_DocumentStructure_fieldAccessorTable.ensureFieldAccessorsInitialized(DocumentStructure.class, Builder.class);
|
||||
}
|
||||
|
||||
|
||||
// Construct using DocumentStructureOuterClass.DocumentStructure.newBuilder()
|
||||
private Builder() {
|
||||
|
||||
maybeForceBuilderInitialization();
|
||||
}
|
||||
|
||||
|
||||
private Builder(BuilderParent parent) {
|
||||
|
||||
super(parent);
|
||||
maybeForceBuilderInitialization();
|
||||
}
|
||||
|
||||
|
||||
private void maybeForceBuilderInitialization() {
|
||||
|
||||
if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
|
||||
getRootFieldBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Builder clear() {
|
||||
|
||||
super.clear();
|
||||
bitField0_ = 0;
|
||||
root_ = null;
|
||||
if (rootBuilder_ != null) {
|
||||
rootBuilder_.dispose();
|
||||
rootBuilder_ = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() {
|
||||
|
||||
return DocumentStructureProto.internal_static_DocumentStructure_descriptor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DocumentStructure getDefaultInstanceForType() {
|
||||
|
||||
return DocumentStructure.getDefaultInstance();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DocumentStructure build() {
|
||||
|
||||
DocumentStructure result = buildPartial();
|
||||
if (!result.isInitialized()) {
|
||||
throw newUninitializedMessageException(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DocumentStructure buildPartial() {
|
||||
|
||||
DocumentStructure result = new DocumentStructure(this);
|
||||
if (bitField0_ != 0) {
|
||||
buildPartial0(result);
|
||||
}
|
||||
onBuilt();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private void buildPartial0(DocumentStructure result) {
|
||||
|
||||
int from_bitField0_ = bitField0_;
|
||||
int to_bitField0_ = 0;
|
||||
if (((from_bitField0_ & 0x00000001) != 0)) {
|
||||
result.root_ = rootBuilder_ == null ? root_ : rootBuilder_.build();
|
||||
to_bitField0_ |= 0x00000001;
|
||||
}
|
||||
result.bitField0_ |= to_bitField0_;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Builder mergeFrom(com.google.protobuf.Message other) {
|
||||
|
||||
if (other instanceof DocumentStructure) {
|
||||
return mergeFrom((DocumentStructure) other);
|
||||
} else {
|
||||
super.mergeFrom(other);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Builder mergeFrom(DocumentStructure other) {
|
||||
|
||||
if (other == DocumentStructure.getDefaultInstance()) {
|
||||
return this;
|
||||
}
|
||||
if (other.hasRoot()) {
|
||||
mergeRoot(other.getRoot());
|
||||
}
|
||||
this.mergeUnknownFields(other.getUnknownFields());
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final boolean isInitialized() {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Builder mergeFrom(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
|
||||
|
||||
if (extensionRegistry == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
try {
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int tag = input.readTag();
|
||||
switch (tag) {
|
||||
case 0:
|
||||
done = true;
|
||||
break;
|
||||
case 10: {
|
||||
input.readMessage(getRootFieldBuilder().getBuilder(), extensionRegistry);
|
||||
bitField0_ |= 0x00000001;
|
||||
break;
|
||||
} // case 10
|
||||
default: {
|
||||
if (!super.parseUnknownField(input, extensionRegistry, tag)) {
|
||||
done = true; // was an endgroup tag
|
||||
}
|
||||
break;
|
||||
} // default:
|
||||
} // switch (tag)
|
||||
} // while (!done)
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
throw e.unwrapIOException();
|
||||
} finally {
|
||||
onChanged();
|
||||
} // finally
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private int bitField0_;
|
||||
|
||||
private EntryDataProto.EntryData root_;
|
||||
private com.google.protobuf.SingleFieldBuilder<EntryDataProto.EntryData, EntryDataProto.EntryData.Builder, EntryDataProto.EntryDataOrBuilder> rootBuilder_;
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*
|
||||
* @return Whether the root field is set.
|
||||
*/
|
||||
public boolean hasRoot() {
|
||||
|
||||
return ((bitField0_ & 0x00000001) != 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*
|
||||
* @return The root.
|
||||
*/
|
||||
public EntryDataProto.EntryData getRoot() {
|
||||
|
||||
if (rootBuilder_ == null) {
|
||||
return root_ == null ? EntryDataProto.EntryData.getDefaultInstance() : root_;
|
||||
} else {
|
||||
return rootBuilder_.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*/
|
||||
public Builder setRoot(EntryDataProto.EntryData value) {
|
||||
|
||||
if (rootBuilder_ == null) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
root_ = value;
|
||||
} else {
|
||||
rootBuilder_.setMessage(value);
|
||||
}
|
||||
bitField0_ |= 0x00000001;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*/
|
||||
public Builder setRoot(EntryDataProto.EntryData.Builder builderForValue) {
|
||||
|
||||
if (rootBuilder_ == null) {
|
||||
root_ = builderForValue.build();
|
||||
} else {
|
||||
rootBuilder_.setMessage(builderForValue.build());
|
||||
}
|
||||
bitField0_ |= 0x00000001;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*/
|
||||
public Builder mergeRoot(EntryDataProto.EntryData value) {
|
||||
|
||||
if (rootBuilder_ == null) {
|
||||
if (((bitField0_ & 0x00000001) != 0) && root_ != null && root_ != EntryDataProto.EntryData.getDefaultInstance()) {
|
||||
getRootBuilder().mergeFrom(value);
|
||||
} else {
|
||||
root_ = value;
|
||||
}
|
||||
} else {
|
||||
rootBuilder_.mergeFrom(value);
|
||||
}
|
||||
if (root_ != null) {
|
||||
bitField0_ |= 0x00000001;
|
||||
onChanged();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*/
|
||||
public Builder clearRoot() {
|
||||
|
||||
bitField0_ = (bitField0_ & ~0x00000001);
|
||||
root_ = null;
|
||||
if (rootBuilder_ != null) {
|
||||
rootBuilder_.dispose();
|
||||
rootBuilder_ = null;
|
||||
}
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*/
|
||||
public EntryDataProto.EntryData.Builder getRootBuilder() {
|
||||
|
||||
bitField0_ |= 0x00000001;
|
||||
onChanged();
|
||||
return getRootFieldBuilder().getBuilder();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*/
|
||||
public EntryDataProto.EntryDataOrBuilder getRootOrBuilder() {
|
||||
|
||||
if (rootBuilder_ != null) {
|
||||
return rootBuilder_.getMessageOrBuilder();
|
||||
} else {
|
||||
return root_ == null ? EntryDataProto.EntryData.getDefaultInstance() : root_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The root EntryData represents the Document.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.EntryData root = 1;</code>
|
||||
*/
|
||||
private com.google.protobuf.SingleFieldBuilder<EntryDataProto.EntryData, EntryDataProto.EntryData.Builder, EntryDataProto.EntryDataOrBuilder> getRootFieldBuilder() {
|
||||
|
||||
if (rootBuilder_ == null) {
|
||||
rootBuilder_ = new com.google.protobuf.SingleFieldBuilder<EntryDataProto.EntryData, EntryDataProto.EntryData.Builder, EntryDataProto.EntryDataOrBuilder>(getRoot(),
|
||||
getParentForChildren(),
|
||||
isClean());
|
||||
root_ = null;
|
||||
}
|
||||
return rootBuilder_;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:DocumentStructure)
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(class_scope:DocumentStructure)
|
||||
private static final DocumentStructure DEFAULT_INSTANCE;
|
||||
|
||||
static {
|
||||
DEFAULT_INSTANCE = new DocumentStructure();
|
||||
}
|
||||
|
||||
public static DocumentStructure getDefaultInstance() {
|
||||
|
||||
return DEFAULT_INSTANCE;
|
||||
}
|
||||
|
||||
|
||||
private static final com.google.protobuf.Parser<DocumentStructure> PARSER = new com.google.protobuf.AbstractParser<DocumentStructure>() {
|
||||
@Override
|
||||
public DocumentStructure parsePartialFrom(com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
|
||||
Builder builder = newBuilder();
|
||||
try {
|
||||
builder.mergeFrom(input, extensionRegistry);
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(builder.buildPartial());
|
||||
} catch (com.google.protobuf.UninitializedMessageException e) {
|
||||
throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
|
||||
} catch (java.io.IOException e) {
|
||||
throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(builder.buildPartial());
|
||||
}
|
||||
return builder.buildPartial();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public static com.google.protobuf.Parser<DocumentStructure> parser() {
|
||||
|
||||
return PARSER;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public com.google.protobuf.Parser<DocumentStructure> getParserForType() {
|
||||
|
||||
return PARSER;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DocumentStructure getDefaultInstanceForType() {
|
||||
|
||||
return DEFAULT_INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final com.google.protobuf.Descriptors.Descriptor internal_static_DocumentStructure_descriptor;
|
||||
private static final com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_DocumentStructure_fieldAccessorTable;
|
||||
|
||||
|
||||
public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
|
||||
private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
|
||||
|
||||
static {
|
||||
String[] descriptorData = {"\n\027DocumentStructure.proto\032\017EntryData.pro"
|
||||
+ "to\"-\n\021DocumentStructure\022\030\n\004root\030\001 \001(\0132\n."
|
||||
+ "EntryDatab\006proto3"};
|
||||
descriptor = com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(descriptorData,
|
||||
new com.google.protobuf.Descriptors.FileDescriptor[]{EntryDataProto.getDescriptor(),});
|
||||
internal_static_DocumentStructure_descriptor = getDescriptor().getMessageTypes()
|
||||
.get(0);
|
||||
internal_static_DocumentStructure_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable(internal_static_DocumentStructure_descriptor,
|
||||
new String[]{"Root",});
|
||||
descriptor.resolveAllFeaturesImmutable();
|
||||
EntryDataProto.getDescriptor();
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(outer_class_scope)
|
||||
}
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
package com.knecon.fforesight.service.layoutparser.internal.api.data.redaction;
|
||||
|
||||
import static com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructureProto.DocumentStructure;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.EntryDataProto.EntryData;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class DocumentStructureWrapper implements Serializable {
|
||||
|
||||
private final DocumentStructure documentStructure;
|
||||
|
||||
|
||||
@Schema(description = "Object containing the extra field names, a table has in its properties field.")
|
||||
public static class TableProperties implements Serializable {
|
||||
|
||||
public static final String NUMBER_OF_ROWS = "numberOfRows";
|
||||
public static final String NUMBER_OF_COLS = "numberOfCols";
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "Object containing the extra field names, an Image has in its properties field.")
|
||||
public static class ImageProperties implements Serializable {
|
||||
|
||||
public static final String TRANSPARENT = "transparent";
|
||||
public static final String IMAGE_TYPE = "imageType";
|
||||
public static final String POSITION = "position";
|
||||
public static final String ID = "id";
|
||||
|
||||
public static final String REPRESENTATION_HASH = "representationHash";
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "Object containing the extra field names, a table cell has in its properties field.")
|
||||
public static class TableCellProperties implements Serializable {
|
||||
|
||||
public static final String B_BOX = "bBox";
|
||||
public static final String ROW = "row";
|
||||
public static final String COL = "col";
|
||||
public static final String HEADER = "header";
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "Object containing the extra field names, a duplicate paragraph has in its properties field.")
|
||||
public static class DuplicateParagraphProperties implements Serializable {
|
||||
|
||||
public static final String UNSORTED_TEXTBLOCK_ID = "utbid";
|
||||
|
||||
}
|
||||
|
||||
public static final String RECTANGLE_DELIMITER = ";";
|
||||
|
||||
|
||||
public static Rectangle2D parseRectangle2D(String bBox) {
|
||||
|
||||
List<Float> floats = Arrays.stream(bBox.split(RECTANGLE_DELIMITER))
|
||||
.map(Float::parseFloat)
|
||||
.toList();
|
||||
return new Rectangle2D.Float(floats.get(0), floats.get(1), floats.get(2), floats.get(3));
|
||||
}
|
||||
|
||||
|
||||
public static double[] parseRepresentationVector(String representationHash) {
|
||||
|
||||
String[] stringArray = representationHash.split("[,\\s]+");
|
||||
double[] doubleArray = new double[stringArray.length];
|
||||
for (int i = 0; i < stringArray.length; i++) {
|
||||
doubleArray[i] = Double.parseDouble(stringArray[i]);
|
||||
}
|
||||
|
||||
return doubleArray;
|
||||
}
|
||||
|
||||
|
||||
public EntryData get(List<Integer> tocId) {
|
||||
|
||||
if (tocId.isEmpty()) {
|
||||
return documentStructure.getRoot();
|
||||
}
|
||||
EntryData entry = documentStructure.getRoot().getChildrenList()
|
||||
.get(tocId.get(0));
|
||||
for (int id : tocId.subList(1, tocId.size())) {
|
||||
entry = entry.getChildrenList()
|
||||
.get(id);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
public Stream<EntryData> streamAllEntries() {
|
||||
|
||||
return Stream.concat(Stream.of(documentStructure.getRoot()),
|
||||
documentStructure.getRoot().getChildrenList()
|
||||
.stream())
|
||||
.flatMap(DocumentStructureWrapper::flatten);
|
||||
}
|
||||
|
||||
|
||||
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.getChildrenList()
|
||||
.stream()
|
||||
.flatMap(DocumentStructureWrapper::flatten));
|
||||
}
|
||||
|
||||
}
|
||||
-1
@@ -10,7 +10,6 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Deprecated
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
|
||||
-2424
File diff suppressed because it is too large
Load Diff
-2977
File diff suppressed because it is too large
Load Diff
-1
@@ -1,6 +1,5 @@
|
||||
package com.knecon.fforesight.service.layoutparser.internal.api.data.redaction;
|
||||
|
||||
@Deprecated
|
||||
public enum LayoutEngine {
|
||||
ALGORITHM,
|
||||
AI,
|
||||
|
||||
-193
@@ -1,193 +0,0 @@
|
||||
package com.knecon.fforesight.service.layoutparser.internal.api.data.redaction;
|
||||
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// NO CHECKED-IN PROTOBUF GENCODE
|
||||
// source: LayoutEngine.proto
|
||||
// Protobuf Java Version: 4.27.1
|
||||
@SuppressWarnings("all")
|
||||
public final class LayoutEngineProto {
|
||||
|
||||
private LayoutEngineProto() {}
|
||||
|
||||
|
||||
static {
|
||||
com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
|
||||
/* major= */ 4,
|
||||
/* minor= */ 27,
|
||||
/* patch= */ 1,
|
||||
/* suffix= */ "", LayoutEngineProto.class.getName());
|
||||
}
|
||||
|
||||
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) {
|
||||
|
||||
registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Protobuf enum {@code LayoutEngine}
|
||||
*/
|
||||
public enum LayoutEngine implements com.google.protobuf.ProtocolMessageEnum {
|
||||
/**
|
||||
* <code>ALGORITHM = 0;</code>
|
||||
*/
|
||||
ALGORITHM(0),
|
||||
/**
|
||||
* <code>AI = 1;</code>
|
||||
*/
|
||||
AI(1),
|
||||
/**
|
||||
* <code>OUTLINE = 2;</code>
|
||||
*/
|
||||
OUTLINE(2),
|
||||
UNRECOGNIZED(-1),
|
||||
;
|
||||
|
||||
|
||||
static {
|
||||
com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
|
||||
/* major= */ 4,
|
||||
/* minor= */ 27,
|
||||
/* patch= */ 1,
|
||||
/* suffix= */ "", LayoutEngine.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>ALGORITHM = 0;</code>
|
||||
*/
|
||||
public static final int ALGORITHM_VALUE = 0;
|
||||
/**
|
||||
* <code>AI = 1;</code>
|
||||
*/
|
||||
public static final int AI_VALUE = 1;
|
||||
/**
|
||||
* <code>OUTLINE = 2;</code>
|
||||
*/
|
||||
public static final int OUTLINE_VALUE = 2;
|
||||
|
||||
|
||||
public final int getNumber() {
|
||||
|
||||
if (this == UNRECOGNIZED) {
|
||||
throw new IllegalArgumentException("Can't get the number of an unknown enum value.");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param value The numeric wire value of the corresponding enum entry.
|
||||
* @return The enum associated with the given numeric wire value.
|
||||
* @deprecated Use {@link #forNumber(int)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static LayoutEngine valueOf(int value) {
|
||||
|
||||
return forNumber(value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param value The numeric wire value of the corresponding enum entry.
|
||||
* @return The enum associated with the given numeric wire value.
|
||||
*/
|
||||
public static LayoutEngine forNumber(int value) {
|
||||
|
||||
switch (value) {
|
||||
case 0:
|
||||
return ALGORITHM;
|
||||
case 1:
|
||||
return AI;
|
||||
case 2:
|
||||
return OUTLINE;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static com.google.protobuf.Internal.EnumLiteMap<LayoutEngine> internalGetValueMap() {
|
||||
|
||||
return internalValueMap;
|
||||
}
|
||||
|
||||
|
||||
private static final com.google.protobuf.Internal.EnumLiteMap<LayoutEngine> internalValueMap = new com.google.protobuf.Internal.EnumLiteMap<LayoutEngine>() {
|
||||
public LayoutEngine findValueByNumber(int number) {
|
||||
|
||||
return LayoutEngine.forNumber(number);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public final com.google.protobuf.Descriptors.EnumValueDescriptor getValueDescriptor() {
|
||||
|
||||
if (this == UNRECOGNIZED) {
|
||||
throw new IllegalStateException("Can't get the descriptor of an unrecognized enum value.");
|
||||
}
|
||||
return getDescriptor().getValues()
|
||||
.get(ordinal());
|
||||
}
|
||||
|
||||
|
||||
public final com.google.protobuf.Descriptors.EnumDescriptor getDescriptorForType() {
|
||||
|
||||
return getDescriptor();
|
||||
}
|
||||
|
||||
|
||||
public static final com.google.protobuf.Descriptors.EnumDescriptor getDescriptor() {
|
||||
|
||||
return LayoutEngineProto.getDescriptor().getEnumTypes()
|
||||
.get(0);
|
||||
}
|
||||
|
||||
|
||||
private static final LayoutEngine[] VALUES = values();
|
||||
|
||||
|
||||
public static LayoutEngine valueOf(com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
|
||||
|
||||
if (desc.getType() != getDescriptor()) {
|
||||
throw new IllegalArgumentException("EnumValueDescriptor is not for this type.");
|
||||
}
|
||||
if (desc.getIndex() == -1) {
|
||||
return UNRECOGNIZED;
|
||||
}
|
||||
return VALUES[desc.getIndex()];
|
||||
}
|
||||
|
||||
|
||||
private final int value;
|
||||
|
||||
|
||||
private LayoutEngine(int value) {
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(enum_scope:LayoutEngine)
|
||||
}
|
||||
|
||||
|
||||
public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
|
||||
private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
|
||||
|
||||
static {
|
||||
String[] descriptorData = {"\n\022LayoutEngine.proto*2\n\014LayoutEngine\022\r\n\t" + "ALGORITHM\020\000\022\006\n\002AI\020\001\022\013\n\007OUTLINE\020\002b\006proto3"};
|
||||
descriptor = com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[]{});
|
||||
descriptor.resolveAllFeaturesImmutable();
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(outer_class_scope)
|
||||
}
|
||||
-1
@@ -3,7 +3,6 @@ package com.knecon.fforesight.service.layoutparser.internal.api.data.redaction;
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
|
||||
@Deprecated
|
||||
public enum NodeType implements Serializable {
|
||||
DOCUMENT,
|
||||
SECTION,
|
||||
|
||||
-274
@@ -1,274 +0,0 @@
|
||||
package com.knecon.fforesight.service.layoutparser.internal.api.data.redaction;
|
||||
|
||||
import java.util.Locale;
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// NO CHECKED-IN PROTOBUF GENCODE
|
||||
// source: NodeType.proto
|
||||
// Protobuf Java Version: 4.27.1
|
||||
@SuppressWarnings("all")
|
||||
public final class NodeTypeProto {
|
||||
|
||||
private NodeTypeProto() {}
|
||||
|
||||
|
||||
static {
|
||||
com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
|
||||
/* major= */ 4,
|
||||
/* minor= */ 27,
|
||||
/* patch= */ 1,
|
||||
/* suffix= */ "", NodeTypeProto.class.getName());
|
||||
}
|
||||
|
||||
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) {
|
||||
|
||||
registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Protobuf enum {@code NodeType}
|
||||
*/
|
||||
public enum NodeType implements com.google.protobuf.ProtocolMessageEnum {
|
||||
/**
|
||||
* <code>DOCUMENT = 0;</code>
|
||||
*/
|
||||
DOCUMENT(0),
|
||||
/**
|
||||
* <code>SECTION = 1;</code>
|
||||
*/
|
||||
SECTION(1),
|
||||
/**
|
||||
* <code>SUPER_SECTION = 2;</code>
|
||||
*/
|
||||
SUPER_SECTION(2),
|
||||
/**
|
||||
* <code>HEADLINE = 3;</code>
|
||||
*/
|
||||
HEADLINE(3),
|
||||
/**
|
||||
* <code>PARAGRAPH = 4;</code>
|
||||
*/
|
||||
PARAGRAPH(4),
|
||||
/**
|
||||
* <code>TABLE = 5;</code>
|
||||
*/
|
||||
TABLE(5),
|
||||
/**
|
||||
* <code>TABLE_CELL = 6;</code>
|
||||
*/
|
||||
TABLE_CELL(6),
|
||||
/**
|
||||
* <code>IMAGE = 7;</code>
|
||||
*/
|
||||
IMAGE(7),
|
||||
/**
|
||||
* <code>HEADER = 8;</code>
|
||||
*/
|
||||
HEADER(8),
|
||||
/**
|
||||
* <code>FOOTER = 9;</code>
|
||||
*/
|
||||
FOOTER(9),
|
||||
UNRECOGNIZED(-1),
|
||||
;
|
||||
|
||||
|
||||
public String toString() {
|
||||
|
||||
return this.name().charAt(0) + this.name().substring(1).toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
|
||||
static {
|
||||
com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
|
||||
/* major= */ 4,
|
||||
/* minor= */ 27,
|
||||
/* patch= */ 1,
|
||||
/* suffix= */ "", NodeType.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>DOCUMENT = 0;</code>
|
||||
*/
|
||||
public static final int DOCUMENT_VALUE = 0;
|
||||
/**
|
||||
* <code>SECTION = 1;</code>
|
||||
*/
|
||||
public static final int SECTION_VALUE = 1;
|
||||
/**
|
||||
* <code>SUPER_SECTION = 2;</code>
|
||||
*/
|
||||
public static final int SUPER_SECTION_VALUE = 2;
|
||||
/**
|
||||
* <code>HEADLINE = 3;</code>
|
||||
*/
|
||||
public static final int HEADLINE_VALUE = 3;
|
||||
/**
|
||||
* <code>PARAGRAPH = 4;</code>
|
||||
*/
|
||||
public static final int PARAGRAPH_VALUE = 4;
|
||||
/**
|
||||
* <code>TABLE = 5;</code>
|
||||
*/
|
||||
public static final int TABLE_VALUE = 5;
|
||||
/**
|
||||
* <code>TABLE_CELL = 6;</code>
|
||||
*/
|
||||
public static final int TABLE_CELL_VALUE = 6;
|
||||
/**
|
||||
* <code>IMAGE = 7;</code>
|
||||
*/
|
||||
public static final int IMAGE_VALUE = 7;
|
||||
/**
|
||||
* <code>HEADER = 8;</code>
|
||||
*/
|
||||
public static final int HEADER_VALUE = 8;
|
||||
/**
|
||||
* <code>FOOTER = 9;</code>
|
||||
*/
|
||||
public static final int FOOTER_VALUE = 9;
|
||||
|
||||
|
||||
public final int getNumber() {
|
||||
|
||||
if (this == UNRECOGNIZED) {
|
||||
throw new IllegalArgumentException("Can't get the number of an unknown enum value.");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param value The numeric wire value of the corresponding enum entry.
|
||||
* @return The enum associated with the given numeric wire value.
|
||||
* @deprecated Use {@link #forNumber(int)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static NodeType valueOf(int value) {
|
||||
|
||||
return forNumber(value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param value The numeric wire value of the corresponding enum entry.
|
||||
* @return The enum associated with the given numeric wire value.
|
||||
*/
|
||||
public static NodeType forNumber(int value) {
|
||||
|
||||
switch (value) {
|
||||
case 0:
|
||||
return DOCUMENT;
|
||||
case 1:
|
||||
return SECTION;
|
||||
case 2:
|
||||
return SUPER_SECTION;
|
||||
case 3:
|
||||
return HEADLINE;
|
||||
case 4:
|
||||
return PARAGRAPH;
|
||||
case 5:
|
||||
return TABLE;
|
||||
case 6:
|
||||
return TABLE_CELL;
|
||||
case 7:
|
||||
return IMAGE;
|
||||
case 8:
|
||||
return HEADER;
|
||||
case 9:
|
||||
return FOOTER;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static com.google.protobuf.Internal.EnumLiteMap<NodeType> internalGetValueMap() {
|
||||
|
||||
return internalValueMap;
|
||||
}
|
||||
|
||||
|
||||
private static final com.google.protobuf.Internal.EnumLiteMap<NodeType> internalValueMap = new com.google.protobuf.Internal.EnumLiteMap<NodeType>() {
|
||||
public NodeType findValueByNumber(int number) {
|
||||
|
||||
return NodeType.forNumber(number);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public final com.google.protobuf.Descriptors.EnumValueDescriptor getValueDescriptor() {
|
||||
|
||||
if (this == UNRECOGNIZED) {
|
||||
throw new IllegalStateException("Can't get the descriptor of an unrecognized enum value.");
|
||||
}
|
||||
return getDescriptor().getValues()
|
||||
.get(ordinal());
|
||||
}
|
||||
|
||||
|
||||
public final com.google.protobuf.Descriptors.EnumDescriptor getDescriptorForType() {
|
||||
|
||||
return getDescriptor();
|
||||
}
|
||||
|
||||
|
||||
public static final com.google.protobuf.Descriptors.EnumDescriptor getDescriptor() {
|
||||
|
||||
return NodeTypeProto.getDescriptor().getEnumTypes()
|
||||
.get(0);
|
||||
}
|
||||
|
||||
|
||||
private static final NodeType[] VALUES = values();
|
||||
|
||||
|
||||
public static NodeType valueOf(com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
|
||||
|
||||
if (desc.getType() != getDescriptor()) {
|
||||
throw new IllegalArgumentException("EnumValueDescriptor is not for this type.");
|
||||
}
|
||||
if (desc.getIndex() == -1) {
|
||||
return UNRECOGNIZED;
|
||||
}
|
||||
return VALUES[desc.getIndex()];
|
||||
}
|
||||
|
||||
|
||||
private final int value;
|
||||
|
||||
|
||||
private NodeType(int value) {
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(enum_scope:NodeType)
|
||||
}
|
||||
|
||||
|
||||
public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
|
||||
private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
|
||||
|
||||
static {
|
||||
String[] descriptorData = {"\n\016NodeType.proto*\223\001\n\010NodeType\022\014\n\010DOCUMEN"
|
||||
+ "T\020\000\022\013\n\007SECTION\020\001\022\021\n\rSUPER_SECTION\020\002\022\014\n\010H"
|
||||
+ "EADLINE\020\003\022\r\n\tPARAGRAPH\020\004\022\t\n\005TABLE\020\005\022\016\n\nT"
|
||||
+ "ABLE_CELL\020\006\022\t\n\005IMAGE\020\007\022\n\n\006HEADER\020\010\022\n\n\006FO"
|
||||
+ "OTER\020\tb\006proto3"};
|
||||
descriptor = com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[]{});
|
||||
descriptor.resolveAllFeaturesImmutable();
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(outer_class_scope)
|
||||
}
|
||||
-9
@@ -21,14 +21,5 @@ public class SimplifiedText {
|
||||
@Schema(description = "A List of simplified Sections, which contains almost exclusively text.")
|
||||
@Builder.Default
|
||||
private List<SimplifiedSectionText> sectionTexts = new ArrayList<>();
|
||||
@Schema(description = "A list of the main section numbers ")
|
||||
@Builder.Default
|
||||
private List<String> mainSectionNumbers = new ArrayList<>();
|
||||
@Schema(description = "A list of the header section numbers ")
|
||||
@Builder.Default
|
||||
private List<String> headerSectionNumbers = new ArrayList<>();
|
||||
@Schema(description = "A list of the footer section numbers ")
|
||||
@Builder.Default
|
||||
private List<String> footerSectionNumbers = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
||||
+2
-5
@@ -2,9 +2,6 @@ package com.knecon.fforesight.service.layoutparser.internal.api.queue;
|
||||
|
||||
public class LayoutParsingQueueNames {
|
||||
|
||||
public static final String LAYOUT_PARSING_REQUEST_QUEUE_PREFIX = "layout_parsing_request";
|
||||
public static final String LAYOUT_PARSING_REQUEST_EXCHANGE = "layout_parsing_request_exchange";
|
||||
public static final String LAYOUT_PARSING_RESPONSE_QUEUE_PREFIX = "layout_parsing_response";
|
||||
public static final String LAYOUT_PARSING_RESPONSE_EXCHANGE = "layout_parsing_response_exchange";
|
||||
public static final String LAYOUT_PARSING_DLQ = "layout_parsing_error";
|
||||
public static final String LAYOUT_PARSING_REQUEST_QUEUE = "layout_parsing_request_queue";
|
||||
public static final String LAYOUT_PARSING_FINISHED_EVENT_QUEUE = "layout_parsing_response_queue";
|
||||
}
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
message AllDocumentPages {
|
||||
|
||||
repeated DocumentPage documentPages = 1;
|
||||
}
|
||||
|
||||
message DocumentPage {
|
||||
// The page number, starting with 1.
|
||||
int32 number = 1;
|
||||
|
||||
// The page height in PDF user units.
|
||||
int32 height = 2;
|
||||
|
||||
// The page width in PDF user units.
|
||||
int32 width = 3;
|
||||
|
||||
// The page rotation as specified by the PDF.
|
||||
int32 rotation = 4;
|
||||
}
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
message AllDocumentPositionData {
|
||||
|
||||
repeated DocumentPositionData documentPositionData = 1;
|
||||
}
|
||||
|
||||
message DocumentPositionData {
|
||||
// Identifier of the text block.
|
||||
int64 id = 1;
|
||||
|
||||
// For each string coordinate in the search text of the text block, the array contains an entry relating the string coordinate to the position coordinate.
|
||||
// This is required due to the text and position coordinates not being equal.
|
||||
repeated int32 stringIdxToPositionIdx = 2;
|
||||
|
||||
// The bounding box for each glyph as a rectangle. This matrix is of size (n,4), where n is the number of glyphs in the text block.
|
||||
// The second dimension specifies the rectangle with the value x, y, width, height, with x, y specifying the lower left corner.
|
||||
// In order to access this information, the stringIdxToPositionIdx array must be used to transform the coordinates.
|
||||
repeated Position positions = 3;
|
||||
|
||||
// Definition of a BoundingBox that contains x, y, width, and height.
|
||||
message Position {
|
||||
repeated float value = 1;
|
||||
}
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
import "EntryData.proto";
|
||||
|
||||
message DocumentStructure {
|
||||
// The root EntryData represents the Document.
|
||||
EntryData root = 1;
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
message AllDocumentTextData {
|
||||
|
||||
repeated DocumentTextData documentTextData = 1;
|
||||
}
|
||||
|
||||
message DocumentTextData {
|
||||
// Identifier of the text block.
|
||||
int64 id = 1;
|
||||
|
||||
// The page the text block occurs on.
|
||||
int64 page = 2;
|
||||
|
||||
// The text of the text block.
|
||||
string searchText = 3;
|
||||
|
||||
// Each text block is assigned a number on a page, starting from 0.
|
||||
int32 numberOnPage = 4;
|
||||
|
||||
// The text blocks are ordered, this number represents the start of the text block as a string offset.
|
||||
int32 start = 5;
|
||||
|
||||
// The text blocks are ordered, this number represents the end of the text block as a string offset.
|
||||
int32 end = 6;
|
||||
|
||||
// The line breaks in the text of this semantic node in string offsets. They are exclusive end. At the end of each semantic node there is an implicit linebreak.
|
||||
repeated int32 lineBreaks = 7;
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
import "LayoutEngine.proto";
|
||||
import "NodeType.proto";
|
||||
|
||||
message EntryData {
|
||||
// Type of the semantic node.
|
||||
NodeType type = 1;
|
||||
|
||||
// Specifies the position in the parsed tree structure.
|
||||
repeated int32 treeId = 2;
|
||||
|
||||
// Specifies the text block IDs associated with this semantic node.
|
||||
repeated int64 atomicBlockIds = 3;
|
||||
|
||||
// Specifies the pages this semantic node appears on.
|
||||
repeated int64 pageNumbers = 4;
|
||||
|
||||
// Some semantic nodes have additional information, this information is stored in this Map.
|
||||
map<string, string> properties = 5;
|
||||
|
||||
// All child Entries of this Entry.
|
||||
repeated EntryData children = 6;
|
||||
|
||||
// Describes the origin of the semantic node.
|
||||
repeated LayoutEngine engines = 7;
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
enum LayoutEngine {
|
||||
ALGORITHM = 0;
|
||||
AI = 1;
|
||||
OUTLINE = 2;
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
enum NodeType {
|
||||
DOCUMENT = 0;
|
||||
SECTION = 1;
|
||||
SUPER_SECTION = 2;
|
||||
HEADLINE = 3;
|
||||
PARAGRAPH = 4;
|
||||
TABLE = 5;
|
||||
TABLE_CELL = 6;
|
||||
IMAGE = 7;
|
||||
HEADER = 8;
|
||||
FOOTER = 9;
|
||||
}
|
||||
@@ -12,14 +12,12 @@ dependencies {
|
||||
implementation(project(":layoutparser-service-internal-api"))
|
||||
implementation(project(":viewer-doc-processor"))
|
||||
|
||||
implementation("com.iqser.red.service:persistence-service-shared-api-v1:2.564.0-RED9010.0") {
|
||||
implementation("com.iqser.red.service:persistence-service-shared-api-v1:2.144.0") {
|
||||
exclude("org.springframework.boot", "spring-boot-starter-security")
|
||||
exclude("org.springframework.boot", "spring-boot-starter-validation")
|
||||
}
|
||||
implementation("com.knecon.fforesight:tenant-commons:0.30.0") {
|
||||
exclude("com.iqser.red.commons", "storage-commons")
|
||||
}
|
||||
implementation("com.iqser.red.commons:storage-commons:2.50.0")
|
||||
implementation("com.knecon.fforesight:tenant-commons:0.21.0")
|
||||
implementation("com.iqser.red.commons:storage-commons:2.45.0")
|
||||
|
||||
implementation("org.apache.pdfbox:pdfbox:${pdfBoxVersion}")
|
||||
implementation("org.apache.pdfbox:pdfbox-tools:${pdfBoxVersion}")
|
||||
@@ -36,5 +34,4 @@ dependencies {
|
||||
implementation("com.pdftron:PDFNet:10.11.0")
|
||||
implementation("org.apache.commons:commons-text:1.12.0")
|
||||
|
||||
implementation("com.google.protobuf:protobuf-java-util:4.27.1")
|
||||
}
|
||||
|
||||
+10
-15
@@ -22,14 +22,13 @@ import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDMarkedContent;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingFinishedEvent;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingRequest;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.RedTextPosition;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextDirection;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.classification.ClassificationService;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.classification.TableOfContentsClassificationService;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.mapper.MarkdownMapper;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.AbstractPageBlock;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationDocument;
|
||||
@@ -45,7 +44,7 @@ import com.knecon.fforesight.service.layoutparser.processor.model.outline.TableO
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.table.Cell;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings;
|
||||
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.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.python_api.adapter.CvTableParsingAdapter;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.python_api.adapter.ImageServiceResponseAdapter;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.python_api.adapter.VisualLayoutParsingAdapter;
|
||||
@@ -53,7 +52,6 @@ import com.knecon.fforesight.service.layoutparser.processor.python_api.model.ima
|
||||
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableCells;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableServiceResponse;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.VisualLayoutParsingResponse;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.BodyTextFrameService;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.RulingCleaningService;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.SectionsBuilderService;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.SimplifiedSectionTextService;
|
||||
@@ -63,9 +61,6 @@ import com.knecon.fforesight.service.layoutparser.processor.services.blockificat
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.blockification.DocstrumBlockificationService;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.blockification.DocuMineBlockificationService;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.blockification.RedactManagerBlockificationService;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.classification.ClarifyndClassificationService;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.classification.DocuMineClassificationService;
|
||||
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.graphics.Box;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.graphics.GraphicExtractorService;
|
||||
@@ -275,7 +270,7 @@ public class LayoutParsingPipeline {
|
||||
stripper.setEndPage(pageNumber);
|
||||
stripper.setPdpage(pdPage);
|
||||
stripper.getText(originDocument);
|
||||
List<TextPositionSequence> words = stripper.getTextPositionSequences();
|
||||
List<Word> words = stripper.getWords();
|
||||
|
||||
// rotateDirAdjExactly(words, pdPage); // works really well for many highly rotated documents (e.g. VV-331340.pdf), but it decreases the headline performance by 1.3%, so I am leaving it out for now
|
||||
|
||||
@@ -301,7 +296,7 @@ public class LayoutParsingPipeline {
|
||||
|
||||
TextRulingsClassifier.classifyUnderlinedAndStrikethroughText(words, cleanRulings);
|
||||
|
||||
List<Box> graphics = graphicExtractorService.extractPathElementGraphics(originDocument, pdPage, pageNumber, cleanRulings, stripper.getTextPositionSequences(), false);
|
||||
List<Box> graphics = graphicExtractorService.extractPathElementGraphics(originDocument, pdPage, pageNumber, cleanRulings, stripper.getWords(), false);
|
||||
|
||||
pdfImages.computeIfAbsent(pageNumber, x -> new ArrayList<>())
|
||||
.addAll(graphics.stream()
|
||||
@@ -314,7 +309,7 @@ public class LayoutParsingPipeline {
|
||||
|
||||
ClassificationPage classificationPage = switch (layoutParsingType) {
|
||||
case REDACT_MANAGER_OLD ->
|
||||
redactManagerBlockificationService.blockify(stripper.getTextPositionSequences(), cleanRulings, classificationDocument.getLayoutDebugLayer());
|
||||
redactManagerBlockificationService.blockify(stripper.getWords(), cleanRulings, classificationDocument.getLayoutDebugLayer());
|
||||
case DOCUMINE_OLD -> docuMineBlockificationService.blockify(words, cleanRulings);
|
||||
case DOCUMINE, REDACT_MANAGER, REDACT_MANAGER_PARAGRAPH_DEBUG, REDACT_MANAGER_WITHOUT_DUPLICATE_PARAGRAPH ->
|
||||
docstrumBlockificationService.blockify(words, cleanRulings, true, classificationDocument.getLayoutDebugLayer(), layoutParsingType);
|
||||
@@ -388,12 +383,12 @@ public class LayoutParsingPipeline {
|
||||
}
|
||||
|
||||
|
||||
private static void rotateDirAdjExactly(List<TextPositionSequence> words, PDPage pdPage) {
|
||||
private static void rotateDirAdjExactly(List<Word> words, PDPage pdPage) {
|
||||
|
||||
for (TextDirection dir : TextDirection.values()) {
|
||||
|
||||
double averageRotation = words.stream()
|
||||
.map(TextPositionSequence::getTextPositions)
|
||||
.map(Word::getTextPositions)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(pos -> pos.getDir().equals(dir))
|
||||
.mapToDouble(RedTextPosition::getExactDir).average().orElse(0);
|
||||
@@ -404,7 +399,7 @@ public class LayoutParsingPipeline {
|
||||
|
||||
AffineTransform rotateInstance = AffineTransform.getRotateInstance(averageRotation, pdPage.getMediaBox().getWidth() / 2, pdPage.getMediaBox().getHeight() / 2);
|
||||
|
||||
for (TextPositionSequence word : words) {
|
||||
for (Word word : words) {
|
||||
if (!dir.equals(word.getDir())) {
|
||||
continue;
|
||||
}
|
||||
@@ -455,10 +450,10 @@ public class LayoutParsingPipeline {
|
||||
// Collect all statistics for the classificationPage, except from blocks inside tables, as tables will always be added to BodyTextFrame.
|
||||
for (AbstractPageBlock textBlock : classificationPage.getTextBlocks()) {
|
||||
if (textBlock instanceof TextPageBlock) {
|
||||
if (((TextPageBlock) textBlock).getSequences() == null) {
|
||||
if (((TextPageBlock) textBlock).getWords() == null) {
|
||||
continue;
|
||||
}
|
||||
for (TextPositionSequence word : ((TextPageBlock) textBlock).getSequences()) {
|
||||
for (Word word : ((TextPageBlock) textBlock).getWords()) {
|
||||
classificationPage.getTextHeightCounter().add(word.getTextHeight());
|
||||
classificationPage.getFontCounter().add(word.getFont());
|
||||
classificationPage.getFontSizeCounter().add(word.getFontSize());
|
||||
|
||||
+4
-30
@@ -11,9 +11,7 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -41,8 +39,6 @@ public class LayoutParsingStorageService {
|
||||
private final StorageService storageService;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private final TaskExecutor taskExecutor;
|
||||
|
||||
|
||||
@Observed(name = "LayoutParsingStorageService", contextualName = "get-origin-file")
|
||||
public File getOriginFile(String storageId) throws IOException {
|
||||
@@ -104,35 +100,13 @@ public class LayoutParsingStorageService {
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@Observed(name = "LayoutParsingStorageService", contextualName = "store-document-data")
|
||||
public void storeDocumentData(LayoutParsingRequest layoutParsingRequest, DocumentData documentData) {
|
||||
|
||||
Runnable storeDocumentStructureRunnable = () -> storageService.storeProtoObject(TenantContext.getTenantId(),
|
||||
layoutParsingRequest.structureFileStorageId(),
|
||||
documentData.getDocumentStructure());
|
||||
|
||||
CompletableFuture<Void> storeDocumentStructureFuture = CompletableFuture.runAsync(storeDocumentStructureRunnable, taskExecutor);
|
||||
|
||||
Runnable storeDocumentTextDataRunnable = () -> storageService.storeProtoObject(TenantContext.getTenantId(),
|
||||
layoutParsingRequest.textBlockFileStorageId(),
|
||||
documentData.getDocumentTextData());
|
||||
|
||||
CompletableFuture<Void> storeDocumentTextDataFuture = CompletableFuture.runAsync(storeDocumentTextDataRunnable, taskExecutor);
|
||||
|
||||
Runnable storeDocumentPositionsRunnable = () -> storageService.storeProtoObject(TenantContext.getTenantId(),
|
||||
layoutParsingRequest.positionBlockFileStorageId(),
|
||||
documentData.getDocumentPositions());
|
||||
|
||||
CompletableFuture<Void> storeDocumentPositionsFuture = CompletableFuture.runAsync(storeDocumentPositionsRunnable, taskExecutor);
|
||||
|
||||
Runnable storeDocumentPagesRunnable = () -> storageService.storeProtoObject(TenantContext.getTenantId(),
|
||||
layoutParsingRequest.pageFileStorageId(),
|
||||
documentData.getDocumentPages());
|
||||
|
||||
CompletableFuture<Void> storeDocumentPagesFuture = CompletableFuture.runAsync(storeDocumentPagesRunnable, taskExecutor);
|
||||
|
||||
CompletableFuture.allOf(storeDocumentStructureFuture, storeDocumentTextDataFuture, storeDocumentPositionsFuture, storeDocumentPagesFuture).join();
|
||||
storageService.storeJSONObject(TenantContext.getTenantId(), layoutParsingRequest.structureFileStorageId(), documentData.getDocumentStructure());
|
||||
storageService.storeJSONObject(TenantContext.getTenantId(), layoutParsingRequest.textBlockFileStorageId(), documentData.getDocumentTextData());
|
||||
storageService.storeJSONObject(TenantContext.getTenantId(), layoutParsingRequest.positionBlockFileStorageId(), documentData.getDocumentPositions());
|
||||
storageService.storeJSONObject(TenantContext.getTenantId(), layoutParsingRequest.pageFileStorageId(), documentData.getDocumentPages());
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -18,7 +18,7 @@ import com.knecon.fforesight.service.layoutparser.processor.docstrum.service.Zon
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.RedTextPosition;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextDirection;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.visualization.LayoutDebugLayer;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -35,7 +35,7 @@ public class DocstrumSegmentationService {
|
||||
private final ReadingOrderService readingOrderService;
|
||||
|
||||
|
||||
public List<Zone> segmentPage(List<TextPositionSequence> textPositions, boolean xyOrder, CleanRulings usedRulings, LayoutDebugLayer visualizations) {
|
||||
public List<Zone> segmentPage(List<Word> textPositions, boolean xyOrder, CleanRulings usedRulings, LayoutDebugLayer visualizations) {
|
||||
|
||||
EnumMap<TextDirection, Integer> directionCounts = new EnumMap<>(TextDirection.class);
|
||||
|
||||
@@ -78,11 +78,11 @@ public class DocstrumSegmentationService {
|
||||
}
|
||||
|
||||
|
||||
private List<Zone> computeZones(List<TextPositionSequence> textPositions, CleanRulings rulings, LayoutDebugLayer visualizations, TextDirection direction) {
|
||||
private List<Zone> computeZones(List<Word> textPositions, CleanRulings rulings, LayoutDebugLayer visualizations, TextDirection direction) {
|
||||
|
||||
List<RedTextPosition> positions = textPositions.stream()
|
||||
.filter(t -> t.getDir() == direction)
|
||||
.map(TextPositionSequence::getTextPositions)
|
||||
.map(Word::getTextPositions)
|
||||
.flatMap(List::stream)
|
||||
.toList();
|
||||
|
||||
|
||||
+13
-10
@@ -1,9 +1,9 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.docstrum.model;
|
||||
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence.BOLD;
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence.BOLD_ITALIC;
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence.ITALIC;
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence.STANDARD;
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.model.text.Word.BOLD;
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.model.text.Word.BOLD_ITALIC;
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.model.text.Word.ITALIC;
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.model.text.Word.STANDARD;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -14,7 +14,7 @@ import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.processor.docstrum.utils.FastAtan2;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.FontStyle;
|
||||
|
||||
import lombok.Data;
|
||||
@@ -24,7 +24,7 @@ import lombok.EqualsAndHashCode;
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
|
||||
public class Line extends TextBoundingBox {
|
||||
|
||||
private static final double WORD_DISTANCE_MULTIPLIER = 0.18;
|
||||
private static final double WORD_DISTANCE_MULTIPLIER = 0.17;
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
private final double x0;
|
||||
@@ -41,7 +41,7 @@ public class Line extends TextBoundingBox {
|
||||
private FontStyle fontStyle;
|
||||
|
||||
private final List<Character> characters;
|
||||
private final List<TextPositionSequence> words = new ArrayList<>();
|
||||
private final List<Word> words = new ArrayList<>();
|
||||
|
||||
|
||||
public Line(List<Character> characters, double wordSpacing) {
|
||||
@@ -89,7 +89,7 @@ public class Line extends TextBoundingBox {
|
||||
for (FontStyle fontStyle : FontStyle.values()) {
|
||||
fontStyleCounter.put(fontStyle, new AtomicInteger(0));
|
||||
}
|
||||
for (TextPositionSequence word : words) {
|
||||
for (Word word : words) {
|
||||
switch (word.getFontStyle()) {
|
||||
case STANDARD -> fontStyleCounter.get(FontStyle.REGULAR).getAndIncrement();
|
||||
case BOLD -> fontStyleCounter.get(FontStyle.BOLD).getAndIncrement();
|
||||
@@ -159,14 +159,17 @@ public class Line extends TextBoundingBox {
|
||||
|
||||
private void computeWords(double wordSpacing) {
|
||||
|
||||
TextPositionSequence word = new TextPositionSequence();
|
||||
// Imo, the width of space should be scaled with the font size, but it only depends on the median distance between horizontal neighbours.
|
||||
// If there are large differences in fontsize on a page, this might lead to missing spaces for the smaller fonts and too many for larger fonts.
|
||||
// I've just now changed the scaling factor. If you come across this comment with missing whitespaces again, try scaling the fontsize instead of simply changing the factor again.
|
||||
Word word = new Word();
|
||||
Character previous = null;
|
||||
for (Character current : characters) {
|
||||
if (previous != null) {
|
||||
double dist = current.getTextPosition().getXDirAdj() - previous.getTextPosition().getXDirAdj() - previous.getTextPosition().getWidthDirAdj();
|
||||
if (dist > wordSpacing) {
|
||||
words.add(word);
|
||||
word = new TextPositionSequence();
|
||||
word = new Word();
|
||||
}
|
||||
}
|
||||
word.getTextPositions().add(current.getTextPosition());
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngineProto.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.docstrum.model.BoundingBox;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock;
|
||||
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ public class FloatFrequencyCounter {
|
||||
mostPopular = entry;
|
||||
}
|
||||
}
|
||||
mostPopularCache = mostPopular != null ? mostPopular.getKey() : null;
|
||||
mostPopularCache = mostPopular != null ? mostPopular.getKey() : 0;
|
||||
changed = false;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -3,7 +3,7 @@ package com.knecon.fforesight.service.layoutparser.processor.model;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.List;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -16,8 +16,8 @@ import lombok.experimental.FieldDefaults;
|
||||
public class LineInformation {
|
||||
|
||||
List<Rectangle2D> lineBBox;
|
||||
List<List<TextPositionSequence>> sequencesByLines;
|
||||
List<List<Word>> sequencesByLines;
|
||||
List<List<Rectangle2D>> bBoxWithGapsByLines;
|
||||
List<List<List<TextPositionSequence>>> sequencesWithGapsByLines;
|
||||
List<List<List<Word>>> sequencesWithGapsByLines;
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ import java.awt.geom.Rectangle2D;
|
||||
import java.util.List;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.table.Ruling;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@@ -15,7 +15,7 @@ import lombok.Getter;
|
||||
@AllArgsConstructor
|
||||
public class PageContents {
|
||||
|
||||
List<TextPositionSequence> sortedTextPositionSequences;
|
||||
List<Word> sortedWords;
|
||||
Rectangle2D cropBox;
|
||||
Rectangle2D mediaBox;
|
||||
List<Ruling> rulings;
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ import lombok.experimental.FieldDefaults;
|
||||
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
||||
public class SectionIdentifier {
|
||||
|
||||
public static Pattern numericalIdentifierPattern = Pattern.compile("^[\\s]?(\\d+)[\\s.,;]?(\\d+)?[\\s.,;]?(\\d+)?[\\s.,;]?(\\d+)?");
|
||||
public static Pattern alphanumericIdentifierPattern = Pattern.compile("^[\\s]?[A-Za-z][\\s.,;]?(\\d+)[\\s.,;]?(\\d+)?[\\s.,;]?(\\d+)?[\\s.,;]?(\\d+)?[\\s.,;]?");
|
||||
public static Pattern numericalIdentifierPattern = Pattern.compile("^[\\s]?(\\d{1,2})(?:[\\s.,;](\\d{1,2}))?(?:[\\s.,;](\\d{1,2}))?(?:[\\s.,;](\\d{1,2}))?");
|
||||
public static Pattern alphanumericIdentifierPattern = Pattern.compile("^[\\s]?[A-Za-z][\\s.,;]?(\\d{1,2})[\\s.,;]?(\\d{1,2})?[\\s.,;]?(\\d{1,2})?[\\s.,;]?(\\d{1,2})?[\\s.,;]?");
|
||||
|
||||
public enum Format {
|
||||
EMPTY,
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.GenericSemanticNode;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.SemanticNode;
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngineProto.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.DocumentTree;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.entity.TextEntity;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.TextBlock;
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.DocumentTree;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.NodeVisitor;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.TextBlock;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.NodeVisitor;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.TextBlock;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.NodeVisitor;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.TextBlock;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.NodeVisitor;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.TextBlock;
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.NodeVisitor;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.TextBlock;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.NodeVisitor;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.TextBlock;
|
||||
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes;
|
||||
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.NodeVisitor;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngineProto.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.DocumentTree;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.TextRange;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.entity.EntityType;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.NodeVisitor;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@ import java.util.Set;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngineProto.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.DocumentTree;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.NodeVisitor;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.entity.TextEntity;
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import java.awt.geom.Rectangle2D;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.NodeVisitor;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.TextBlock;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.TextBlockCollector;
|
||||
|
||||
+7
-14
@@ -1,6 +1,5 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock;
|
||||
|
||||
import static com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentTextDataProto.DocumentTextData;
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
@@ -14,8 +13,8 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPositionDataProto.DocumentPositionData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPositionDataProto.DocumentPositionData.Position;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPositionData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentTextData;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.TextRange;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Page;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.SemanticNode;
|
||||
@@ -154,9 +153,11 @@ public class AtomicTextBlock implements TextBlock {
|
||||
.page(page)
|
||||
.textRange(new TextRange(documentTextData.getStart(), documentTextData.getEnd()))
|
||||
.searchText(documentTextData.getSearchText())
|
||||
.lineBreaks(documentTextData.getLineBreaksList())
|
||||
.stringIdxToPositionIdx(documentPositionData.getStringIdxToPositionIdxList())
|
||||
.positions(toRectangle2DList(documentPositionData.getPositionsList()))
|
||||
.lineBreaks(Arrays.stream(documentTextData.getLineBreaks()).boxed()
|
||||
.toList())
|
||||
.stringIdxToPositionIdx(Arrays.stream(documentPositionData.getStringIdxToPositionIdx()).boxed()
|
||||
.toList())
|
||||
.positions(toRectangle2DList(documentPositionData.getPositions()))
|
||||
.parent(parent)
|
||||
.build();
|
||||
}
|
||||
@@ -170,14 +171,6 @@ public class AtomicTextBlock implements TextBlock {
|
||||
}
|
||||
|
||||
|
||||
private static List<Rectangle2D> toRectangle2DList(List<Position> positions) {
|
||||
|
||||
return positions.stream()
|
||||
.map(pos -> (Rectangle2D) new Rectangle2D.Float(pos.getValue(0), pos.getValue(1), pos.getValue(2), pos.getValue(3)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
public CharSequence getLine(int lineNumber) {
|
||||
|
||||
if (lineNumber >= numberOfLines() || lineNumber < 0) {
|
||||
|
||||
+1
-1
@@ -168,7 +168,7 @@ public class TOCEnrichmentService {
|
||||
}
|
||||
}
|
||||
|
||||
if (!startBlocks.isEmpty() || !startImages.isEmpty()) {
|
||||
if (!startBlocks.isEmpty()) {
|
||||
TableOfContentItem unassigned = new TableOfContentItem(null);
|
||||
unassigned.setSectionBlocks(startBlocks);
|
||||
unassigned.setImages(startImages);
|
||||
|
||||
+3
-3
@@ -9,7 +9,7 @@ import java.util.List;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.processor.docstrum.model.BoundingBox;
|
||||
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.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.utils.TextNormalizationUtilities;
|
||||
|
||||
import lombok.Data;
|
||||
@@ -68,12 +68,12 @@ public class Cell extends BoundingBox {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
Iterator<TextPageBlock> itty = textBlocks.iterator();
|
||||
TextPositionSequence previous = null;
|
||||
Word previous = null;
|
||||
while (itty.hasNext()) {
|
||||
|
||||
TextPageBlock textBlock = itty.next();
|
||||
|
||||
for (TextPositionSequence word : textBlock.getSequences()) {
|
||||
for (Word word : textBlock.getWords()) {
|
||||
if (previous != null) {
|
||||
if (Math.abs(previous.getMaxYDirAdj() - word.getMaxYDirAdj()) > word.getTextHeight()) {
|
||||
sb.append('\n');
|
||||
|
||||
+31
-9
@@ -4,7 +4,6 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -15,15 +14,17 @@ import lombok.experimental.FieldDefaults;
|
||||
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
||||
public class ListIdentifier {
|
||||
|
||||
public static final Pattern STARTING_NUMBERS = Pattern.compile("^\\s*([1-9]+)\\.\\s+");
|
||||
public static final Pattern NUMBER_WITH_DOT = Pattern.compile("^\\s*([1-9]{1,4})\\.\\s+");
|
||||
public static final Pattern NUMBER_IN_PARENTHESES = Pattern.compile("^\\s*\\(([1-9]{1,4})\\)\\s+");
|
||||
|
||||
enum Format {
|
||||
NUMBERS
|
||||
NUMBER_WITH_DOT,
|
||||
NUMBER_IN_PARENTHESES
|
||||
}
|
||||
|
||||
Format format;
|
||||
@Getter
|
||||
TextPositionSequence word;
|
||||
Word word;
|
||||
@Getter
|
||||
int page;
|
||||
int representation;
|
||||
@@ -31,29 +32,50 @@ public class ListIdentifier {
|
||||
|
||||
public static Optional<ListIdentifier> parse(TextPageBlock textPageBlock, int page) {
|
||||
|
||||
return parse(textPageBlock.getSequences().subList(0, Math.min(5, textPageBlock.getSequences().size())), page);
|
||||
return parse(textPageBlock.getWords().subList(0, Math.min(5, textPageBlock.getWords().size())), page);
|
||||
}
|
||||
|
||||
|
||||
public static Optional<ListIdentifier> parse(List<TextPositionSequence> sequences, int page) {
|
||||
public static Optional<ListIdentifier> parse(List<Word> sequences, int page) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (TextPositionSequence sequence : sequences) {
|
||||
for (Word sequence : sequences) {
|
||||
sb.append(sequence.toString());
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.replace(sb.length() - 1, sb.length(), "");
|
||||
String text = sb.toString();
|
||||
|
||||
Matcher numberMatcher = STARTING_NUMBERS.matcher(text);
|
||||
Matcher numberMatcher = NUMBER_WITH_DOT.matcher(text);
|
||||
|
||||
if (numberMatcher.find()) {
|
||||
return Optional.of(new ListIdentifier(Format.NUMBERS, sequences.get(0), page, Integer.parseInt(numberMatcher.group(1))));
|
||||
Optional<Integer> representation = parseInteger(numberMatcher.group(1));
|
||||
if (representation.isPresent()) {
|
||||
return Optional.of(new ListIdentifier(Format.NUMBER_WITH_DOT, sequences.get(0), page, representation.get()));
|
||||
}
|
||||
}
|
||||
|
||||
Matcher parenthesisMatcher = NUMBER_IN_PARENTHESES.matcher(text);
|
||||
if (parenthesisMatcher.find()) {
|
||||
Optional<Integer> representation = parseInteger(parenthesisMatcher.group(1));
|
||||
if (representation.isPresent()) {
|
||||
return Optional.of(new ListIdentifier(Format.NUMBER_IN_PARENTHESES, sequences.get(0), page, representation.get()));
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
private static Optional<Integer> parseInteger(String text) {
|
||||
|
||||
try {
|
||||
return Optional.of(Integer.parseInt(text));
|
||||
} catch (NumberFormatException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean isInOrder(List<ListIdentifier> listIdentifiers) {
|
||||
|
||||
if (listIdentifiers.size() <= 1) {
|
||||
|
||||
+7
-7
@@ -10,18 +10,18 @@ import lombok.Getter;
|
||||
@Getter
|
||||
public class SearchableText {
|
||||
|
||||
private final List<TextPositionSequence> sequences = new ArrayList<>();
|
||||
private final List<Word> sequences = new ArrayList<>();
|
||||
|
||||
|
||||
public void add(TextPositionSequence textPositionSequence) {
|
||||
public void add(Word word) {
|
||||
|
||||
sequences.add(textPositionSequence);
|
||||
sequences.add(word);
|
||||
}
|
||||
|
||||
|
||||
public void addAll(List<TextPositionSequence> textPositionSequences) {
|
||||
public void addAll(List<Word> words) {
|
||||
|
||||
sequences.addAll(textPositionSequences);
|
||||
sequences.addAll(words);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,10 +32,10 @@ public class SearchableText {
|
||||
}
|
||||
|
||||
|
||||
public static String buildString(List<TextPositionSequence> sequences) {
|
||||
public static String buildString(List<Word> sequences) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (TextPositionSequence word : sequences) {
|
||||
for (Word word : sequences) {
|
||||
sb.append(word);
|
||||
sb.append(' ');
|
||||
}
|
||||
|
||||
+37
-36
@@ -25,7 +25,7 @@ import lombok.NoArgsConstructor;
|
||||
public class TextPageBlock extends AbstractPageBlock {
|
||||
|
||||
@Builder.Default
|
||||
private List<TextPositionSequence> sequences = new ArrayList<>();
|
||||
private List<Word> words = new ArrayList<>();
|
||||
@Builder.Default
|
||||
private FrequencyCounters frequencyCounters = new FrequencyCounters();
|
||||
|
||||
@@ -41,43 +41,43 @@ public class TextPageBlock extends AbstractPageBlock {
|
||||
private boolean changed;
|
||||
|
||||
|
||||
public TextPageBlock(List<TextPositionSequence> sequences) {
|
||||
public TextPageBlock(List<Word> words) {
|
||||
|
||||
this.sequences = new ArrayList<>(sequences);
|
||||
this.words = new ArrayList<>(words);
|
||||
this.frequencyCounters = new FrequencyCounters();
|
||||
|
||||
if (!sequences.isEmpty()) {
|
||||
addToFrequencyCounters(sequences);
|
||||
if (!words.isEmpty()) {
|
||||
addToFrequencyCounters(words);
|
||||
}
|
||||
calculateBBox();
|
||||
}
|
||||
|
||||
|
||||
public List<TextPositionSequence> getSequences() {
|
||||
public List<Word> getWords() {
|
||||
|
||||
return Collections.unmodifiableList(sequences);
|
||||
return Collections.unmodifiableList(words);
|
||||
}
|
||||
|
||||
|
||||
public TextDirection getDir() {
|
||||
|
||||
return sequences.get(0).getDir();
|
||||
return words.get(0).getDir();
|
||||
}
|
||||
|
||||
|
||||
private void calculateBBox() {
|
||||
|
||||
if (sequences == null) {
|
||||
if (words == null) {
|
||||
this.bBox = new Rectangle2D.Double();
|
||||
this.bBoxPdf = new Rectangle2D.Double();
|
||||
this.bBoxDirAdj = new Rectangle2D.Double();
|
||||
return;
|
||||
}
|
||||
this.bBoxDirAdj = sequences.stream()
|
||||
.map(TextPositionSequence::getBBoxDirAdj)
|
||||
this.bBoxDirAdj = words.stream()
|
||||
.map(Word::getBBoxDirAdj)
|
||||
.collect(RectangleTransformations.collectBBox());
|
||||
|
||||
setToBBoxOfComponents(sequences);
|
||||
setToBBoxOfComponents(words);
|
||||
}
|
||||
|
||||
|
||||
@@ -99,8 +99,8 @@ public class TextPageBlock extends AbstractPageBlock {
|
||||
throw new IllegalArgumentException("Cannot merge textBlocks on different pages.");
|
||||
}
|
||||
|
||||
List<TextPositionSequence> sequences = textBlocksToMerge.stream()
|
||||
.map(TextPageBlock::getSequences)
|
||||
List<Word> sequences = textBlocksToMerge.stream()
|
||||
.map(TextPageBlock::getWords)
|
||||
.flatMap(java.util.Collection::stream)
|
||||
.toList();
|
||||
sequences = new ArrayList<>(sequences);
|
||||
@@ -109,9 +109,9 @@ public class TextPageBlock extends AbstractPageBlock {
|
||||
}
|
||||
|
||||
|
||||
private void addToFrequencyCounters(List<TextPositionSequence> sequences) {
|
||||
private void addToFrequencyCounters(List<Word> sequences) {
|
||||
|
||||
for (TextPositionSequence wordBlock : sequences) {
|
||||
for (Word wordBlock : sequences) {
|
||||
|
||||
frequencyCounters.getLineHeightFrequencyCounter().add(wordBlock.getTextHeight());
|
||||
frequencyCounters.getFontSizeFrequencyCounter().add(wordBlock.getFontSize());
|
||||
@@ -120,12 +120,12 @@ public class TextPageBlock extends AbstractPageBlock {
|
||||
frequencyCounters.getStyleFrequencyCounter().add(wordBlock.getFontStyle());
|
||||
}
|
||||
|
||||
setUnderlined(this.sequences.stream()
|
||||
.allMatch(TextPositionSequence::isUnderline));
|
||||
setUnderlined(this.words.stream()
|
||||
.allMatch(Word::isUnderline));
|
||||
}
|
||||
|
||||
|
||||
public TextPageBlock union(TextPositionSequence r) {
|
||||
public TextPageBlock union(Word r) {
|
||||
|
||||
TextPageBlock union = this.copy();
|
||||
union.add(r);
|
||||
@@ -138,8 +138,8 @@ public class TextPageBlock extends AbstractPageBlock {
|
||||
public TextPageBlock union(TextPageBlock r) {
|
||||
|
||||
TextPageBlock union = this.copy();
|
||||
union.addAll(r.getSequences());
|
||||
addToFrequencyCounters(r.getSequences());
|
||||
union.addAll(r.getWords());
|
||||
addToFrequencyCounters(r.getWords());
|
||||
calculateBBox();
|
||||
return union;
|
||||
}
|
||||
@@ -148,33 +148,33 @@ public class TextPageBlock extends AbstractPageBlock {
|
||||
public void add(TextPageBlock textPageBlock) {
|
||||
|
||||
changed = true;
|
||||
sequences.addAll(textPageBlock.getSequences());
|
||||
addToFrequencyCounters(textPageBlock.getSequences());
|
||||
words.addAll(textPageBlock.getWords());
|
||||
addToFrequencyCounters(textPageBlock.getWords());
|
||||
calculateBBox();
|
||||
}
|
||||
|
||||
|
||||
public void add(TextPositionSequence textPositionSequence) {
|
||||
public void add(Word word) {
|
||||
|
||||
changed = true;
|
||||
sequences.add(textPositionSequence);
|
||||
addToFrequencyCounters(List.of(textPositionSequence));
|
||||
words.add(word);
|
||||
addToFrequencyCounters(List.of(word));
|
||||
calculateBBox();
|
||||
}
|
||||
|
||||
|
||||
public void addAll(List<TextPositionSequence> textPositionSequences) {
|
||||
public void addAll(List<Word> words) {
|
||||
|
||||
changed = true;
|
||||
sequences.addAll(textPositionSequences);
|
||||
addToFrequencyCounters(textPositionSequences);
|
||||
this.words.addAll(words);
|
||||
addToFrequencyCounters(words);
|
||||
calculateBBox();
|
||||
}
|
||||
|
||||
|
||||
public TextPageBlock copy() {
|
||||
|
||||
return new TextPageBlock(new ArrayList<>(sequences));
|
||||
return new TextPageBlock(new ArrayList<>(words));
|
||||
}
|
||||
|
||||
|
||||
@@ -193,8 +193,8 @@ public class TextPageBlock extends AbstractPageBlock {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
TextPositionSequence previous = null;
|
||||
for (TextPositionSequence word : sequences) {
|
||||
Word previous = null;
|
||||
for (Word word : words) {
|
||||
if (previous != null) {
|
||||
if (Math.abs(previous.getMaxYDirAdj() - word.getMaxYDirAdj()) > word.getTextHeight()) {
|
||||
sb.append('\n');
|
||||
@@ -217,8 +217,8 @@ public class TextPageBlock extends AbstractPageBlock {
|
||||
public int getNumberOfLines() {
|
||||
|
||||
int numberOfLines = 1;
|
||||
TextPositionSequence previous = null;
|
||||
for (TextPositionSequence word : sequences) {
|
||||
Word previous = null;
|
||||
for (Word word : words) {
|
||||
if (previous != null) {
|
||||
if (word.getMaxYDirAdj() - previous.getMaxYDirAdj() > word.getTextHeight()) {
|
||||
numberOfLines++;
|
||||
@@ -262,14 +262,15 @@ public class TextPageBlock extends AbstractPageBlock {
|
||||
|
||||
public double getHighestFontSize() {
|
||||
|
||||
return frequencyCounters.getFontSizeFrequencyCounter().getHighest();
|
||||
Double highest = frequencyCounters.getFontSizeFrequencyCounter().getHighest();
|
||||
return highest == null ? 0 : highest;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
|
||||
return sequences.isEmpty();
|
||||
return words.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.model.text;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class TextPositionSequenceComparator implements Comparator<TextPositionSequence> {
|
||||
|
||||
private HashMap<TextPositionSequence, TextBlockOnPage> lookup;
|
||||
|
||||
|
||||
public TextPositionSequenceComparator(HashMap<TextPositionSequence, TextBlockOnPage> lookup) {
|
||||
|
||||
this.lookup = lookup;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compare(TextPositionSequence number1, TextPositionSequence number2) {
|
||||
|
||||
int page1 = lookup.get(number1).page().getPageNumber();
|
||||
int page2 = lookup.get(number2).page().getPageNumber();
|
||||
|
||||
if (page1 != page2) {
|
||||
return Integer.compare(page1, page2);
|
||||
}
|
||||
|
||||
if (number1.getY() != number2.getY()) {
|
||||
return Double.compare(number1.getY(), number2.getY());
|
||||
}
|
||||
|
||||
return Integer.compare(Integer.parseInt(number1.toString()), Integer.parseInt(number2.toString()));
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.model.text;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.classification.NumberWord;
|
||||
|
||||
public class TocNumberComparator implements Comparator<NumberWord> {
|
||||
|
||||
private HashMap<NumberWord, TextBlockOnPage> lookup;
|
||||
|
||||
|
||||
public TocNumberComparator(HashMap<NumberWord, TextBlockOnPage> lookup) {
|
||||
|
||||
this.lookup = lookup;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compare(NumberWord number1, NumberWord number2) {
|
||||
|
||||
int page1 = lookup.get(number1).page().getPageNumber();
|
||||
int page2 = lookup.get(number2).page().getPageNumber();
|
||||
|
||||
if (page1 != page2) {
|
||||
return Integer.compare(page1, page2);
|
||||
}
|
||||
|
||||
if (number1.word().getY() != number2.word().getY()) {
|
||||
return Double.compare(number1.word().getY(), number2.word().getY());
|
||||
}
|
||||
|
||||
return Integer.compare(number1.number(), number2.number());
|
||||
}
|
||||
|
||||
}
|
||||
+9
-9
@@ -27,7 +27,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@SuppressWarnings("pmd")
|
||||
public class TextPositionSequence extends TextBoundingBox implements CharSequence {
|
||||
public class Word extends TextBoundingBox implements CharSequence {
|
||||
|
||||
public static final String STANDARD = "standard";
|
||||
public static final String BOLD_ITALIC = "bold, italic";
|
||||
@@ -47,7 +47,7 @@ public class TextPositionSequence extends TextBoundingBox implements CharSequenc
|
||||
private Integer hashcodeCache;
|
||||
|
||||
|
||||
public TextPositionSequence(List<TextPosition> textPositions, int pageNumber, boolean isParagraphStart) {
|
||||
public Word(List<TextPosition> textPositions, int pageNumber, boolean isParagraphStart) {
|
||||
|
||||
this.textPositions = textPositions.stream()
|
||||
.map(RedTextPosition::fromTextPosition)
|
||||
@@ -65,7 +65,7 @@ public class TextPositionSequence extends TextBoundingBox implements CharSequenc
|
||||
}
|
||||
|
||||
|
||||
public TextPositionSequence(List<RedTextPosition> textPositions, int page) {
|
||||
public Word(List<RedTextPosition> textPositions, int page) {
|
||||
|
||||
this.textPositions = textPositions;
|
||||
this.page = page;
|
||||
@@ -98,9 +98,9 @@ public class TextPositionSequence extends TextBoundingBox implements CharSequenc
|
||||
|
||||
|
||||
@Override
|
||||
public TextPositionSequence subSequence(int start, int end) {
|
||||
public Word subSequence(int start, int end) {
|
||||
|
||||
var textPositionSequence = new TextPositionSequence();
|
||||
var textPositionSequence = new Word();
|
||||
textPositionSequence.textPositions = textPositions.subList(start, end);
|
||||
textPositionSequence.page = page;
|
||||
textPositionSequence.dir = dir;
|
||||
@@ -126,10 +126,10 @@ public class TextPositionSequence extends TextBoundingBox implements CharSequenc
|
||||
}
|
||||
|
||||
|
||||
public void add(TextPositionSequence textPositionSequence, RedTextPosition textPosition) {
|
||||
public void add(Word word, RedTextPosition textPosition) {
|
||||
|
||||
this.textPositions.add(textPosition);
|
||||
this.page = textPositionSequence.getPage();
|
||||
this.page = word.getPage();
|
||||
calculateBBoxAndHashcode();
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ public class TextPositionSequence extends TextBoundingBox implements CharSequenc
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof TextPositionSequence other)) {
|
||||
if (!(o instanceof Word other)) {
|
||||
return false;
|
||||
}
|
||||
if (!other.canEqual((Object) this)) {
|
||||
@@ -220,7 +220,7 @@ public class TextPositionSequence extends TextBoundingBox implements CharSequenc
|
||||
}
|
||||
|
||||
|
||||
protected boolean canEqual(final Object other) {return other instanceof TextPositionSequence;}
|
||||
protected boolean canEqual(final Object other) {return other instanceof Word;}
|
||||
|
||||
|
||||
public int hashCode() {
|
||||
+2
-2
@@ -23,11 +23,11 @@ public class DividingColumnDetectionService {
|
||||
public List<Rectangle2D> detectColumns(PageContents pageContents) {
|
||||
|
||||
|
||||
if (pageContents.getSortedTextPositionSequences().size() < 2) {
|
||||
if (pageContents.getSortedWords().size() < 2) {
|
||||
return List.of(pageContents.getCropBox());
|
||||
}
|
||||
|
||||
GapInformation linesWithGapInformation = GapDetectionService.findGapsInLines(pageContents.getSortedTextPositionSequences(), pageContents.getCropBox());
|
||||
GapInformation linesWithGapInformation = GapDetectionService.findGapsInLines(pageContents.getSortedWords(), pageContents.getCropBox());
|
||||
|
||||
return detectColumnsFromLines(linesWithGapInformation.getXGaps(), pageContents.getCropBox());
|
||||
}
|
||||
|
||||
+15
-15
@@ -5,7 +5,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.GapInformation;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.experimental.UtilityClass;
|
||||
@@ -18,23 +18,23 @@ public class GapDetectionService {
|
||||
private static final double NEW_LINE_FACTOR = 0.2;
|
||||
|
||||
|
||||
public static GapInformation findGapsInLines(List<TextPositionSequence> sortedTextPositionSequences, Rectangle2D mainBodyTextFrame) {
|
||||
public static GapInformation findGapsInLines(List<Word> sortedWords, Rectangle2D mainBodyTextFrame) {
|
||||
|
||||
if (sortedTextPositionSequences.isEmpty()) {
|
||||
if (sortedWords.isEmpty()) {
|
||||
return new GapInformation();
|
||||
}
|
||||
|
||||
final double avgTextPositionHeight = getAvgTextPositionHeight(sortedTextPositionSequences);
|
||||
final double avgTextPositionHeight = getAvgTextPositionHeight(sortedWords);
|
||||
|
||||
XGapsContext xGapContext = XGapsContext.init(mainBodyTextFrame);
|
||||
YGapsContext yGapContext = YGapsContext.init(mainBodyTextFrame);
|
||||
|
||||
var previousTextPosition = sortedTextPositionSequences.get(0);
|
||||
var previousTextPosition = sortedWords.get(0);
|
||||
Rectangle2D rectangle = toRectangle2D(previousTextPosition);
|
||||
|
||||
xGapContext.addGapFromLeftEdgeOfMainBody(rectangle);
|
||||
|
||||
for (TextPositionSequence currentTextPosition : sortedTextPositionSequences.subList(1, sortedTextPositionSequences.size())) {
|
||||
for (Word currentTextPosition : sortedWords.subList(1, sortedWords.size())) {
|
||||
|
||||
double yDifference = Math.abs(currentTextPosition.getMaxYDirAdj() - previousTextPosition.getMaxYDirAdj());
|
||||
double xGap = Math.abs(previousTextPosition.getMaxXDirAdj() - currentTextPosition.getXDirAdj());
|
||||
@@ -59,14 +59,14 @@ public class GapDetectionService {
|
||||
}
|
||||
previousTextPosition = currentTextPosition;
|
||||
}
|
||||
xGapContext.addGapToRightEdgeOfMainBody(toRectangle2D(sortedTextPositionSequences.get(sortedTextPositionSequences.size() - 1)));
|
||||
xGapContext.addGapToRightEdgeOfMainBody(toRectangle2D(sortedWords.get(sortedWords.size() - 1)));
|
||||
xGapContext.gapsPerLine.add(xGapContext.gapsInCurrentLine);
|
||||
|
||||
return new GapInformation(xGapContext.gapsPerLine, yGapContext.gapsPerLine);
|
||||
}
|
||||
|
||||
|
||||
private static Rectangle2D toRectangle2D(TextPositionSequence textPosition) {
|
||||
private static Rectangle2D toRectangle2D(Word textPosition) {
|
||||
|
||||
return mirrorY(textPosition.getBBox());
|
||||
}
|
||||
@@ -87,18 +87,18 @@ public class GapDetectionService {
|
||||
}
|
||||
|
||||
|
||||
private static void assertAllTextPositionsHaveSameDir(List<TextPositionSequence> textPositionSequences) {
|
||||
private static void assertAllTextPositionsHaveSameDir(List<Word> words) {
|
||||
|
||||
assert textPositionSequences.stream()
|
||||
.map(TextPositionSequence::getDir)
|
||||
.allMatch(a -> a.equals(textPositionSequences.get(0).getDir()));
|
||||
assert words.stream()
|
||||
.map(Word::getDir)
|
||||
.allMatch(a -> a.equals(words.get(0).getDir()));
|
||||
}
|
||||
|
||||
|
||||
private static double getAvgTextPositionHeight(List<TextPositionSequence> textPositionSequences) {
|
||||
private static double getAvgTextPositionHeight(List<Word> words) {
|
||||
|
||||
return textPositionSequences.stream()
|
||||
.mapToDouble(TextPositionSequence::getHeight).average().orElseThrow();
|
||||
return words.stream()
|
||||
.mapToDouble(Word::getHeight).average().orElseThrow();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -7,17 +7,17 @@ import java.util.List;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.GapInformation;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.LineInformation;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class InvisibleTableDetectionService {
|
||||
|
||||
public List<List<Rectangle2D>> detectTable(List<TextPositionSequence> textPositionSequences, Rectangle2D tableBBox) {
|
||||
public List<List<Rectangle2D>> detectTable(List<Word> words, Rectangle2D tableBBox) {
|
||||
|
||||
LineInformation lineInformation = LineDetectionService.calculateLineInformation(textPositionSequences);
|
||||
GapInformation gaps = GapDetectionService.findGapsInLines(textPositionSequences, tableBBox);
|
||||
LineInformation lineInformation = LineDetectionService.calculateLineInformation(words);
|
||||
GapInformation gaps = GapDetectionService.findGapsInLines(words, tableBBox);
|
||||
List<Rectangle2D> gapsAcrossLines = GapsAcrossLinesService.detectXGapsAcrossLines(gaps, tableBBox);
|
||||
List<Double> columnXCoords = gapsAcrossLines.stream().map(RectangularShape::getCenterX).toList();
|
||||
int colCount = gapsAcrossLines.size();
|
||||
|
||||
+30
-30
@@ -7,7 +7,7 @@ import java.util.List;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.GapInformation;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.LineInformation;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.utils.RectangleTransformations;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@@ -19,37 +19,37 @@ public class LineDetectionService {
|
||||
private static final double X_GAP_FACTOR = 1; // multiplied with average text height, determines the minimum distance of gaps in lines
|
||||
|
||||
|
||||
public LineInformation calculateLineInformation(List<TextPositionSequence> sortedTextPositionSequences) {
|
||||
public LineInformation calculateLineInformation(List<Word> sortedWords) {
|
||||
|
||||
if (sortedTextPositionSequences.isEmpty()) {
|
||||
if (sortedWords.isEmpty()) {
|
||||
return LineFactory.init().build();
|
||||
}
|
||||
|
||||
return buildLineInformation(sortedTextPositionSequences);
|
||||
return buildLineInformation(sortedWords);
|
||||
}
|
||||
|
||||
|
||||
public List<List<Rectangle2D>> findLinesWithGaps(List<TextPositionSequence> sortedTextPositionSequences, Rectangle2D mainBodyTextFrame) {
|
||||
public List<List<Rectangle2D>> findLinesWithGaps(List<Word> sortedWords, Rectangle2D mainBodyTextFrame) {
|
||||
|
||||
return calculateLineInformation(sortedTextPositionSequences).getBBoxWithGapsByLines();
|
||||
return calculateLineInformation(sortedWords).getBBoxWithGapsByLines();
|
||||
}
|
||||
|
||||
|
||||
public List<List<TextPositionSequence>> orderByLines(List<TextPositionSequence> sortedTextPositionSequences, Rectangle2D mainBodyTextFrame) {
|
||||
public List<List<Word>> orderByLines(List<Word> sortedWords, Rectangle2D mainBodyTextFrame) {
|
||||
|
||||
return calculateLineInformation(sortedTextPositionSequences).getSequencesByLines();
|
||||
return calculateLineInformation(sortedWords).getSequencesByLines();
|
||||
}
|
||||
|
||||
|
||||
private static LineInformation buildLineInformation(List<TextPositionSequence> sortedTextPositionSequences) {
|
||||
private static LineInformation buildLineInformation(List<Word> sortedWords) {
|
||||
|
||||
final double avgTextPositionHeight = getAvgTextPositionHeight(sortedTextPositionSequences);
|
||||
final double avgTextPositionHeight = getAvgTextPositionHeight(sortedWords);
|
||||
|
||||
LineFactory lineFactory = LineFactory.init();
|
||||
|
||||
var previousTextPosition = sortedTextPositionSequences.get(0);
|
||||
var previousTextPosition = sortedWords.get(0);
|
||||
lineFactory.addToCurrentLine(previousTextPosition);
|
||||
for (TextPositionSequence currentTextPosition : sortedTextPositionSequences.subList(1, sortedTextPositionSequences.size())) {
|
||||
for (Word currentTextPosition : sortedWords.subList(1, sortedWords.size())) {
|
||||
if (isNewLine(currentTextPosition, previousTextPosition, avgTextPositionHeight) || isSplitByOrientation(currentTextPosition, previousTextPosition)) {
|
||||
lineFactory.startNewLine();
|
||||
} else if (isXGap(currentTextPosition, previousTextPosition, avgTextPositionHeight)) {
|
||||
@@ -63,25 +63,25 @@ public class LineDetectionService {
|
||||
}
|
||||
|
||||
|
||||
private static double getAvgTextPositionHeight(List<TextPositionSequence> textPositionSequences) {
|
||||
private static double getAvgTextPositionHeight(List<Word> words) {
|
||||
|
||||
return textPositionSequences.stream().mapToDouble(TextPositionSequence::getHeight).average().orElseThrow();
|
||||
return words.stream().mapToDouble(Word::getHeight).average().orElseThrow();
|
||||
}
|
||||
|
||||
|
||||
private static boolean isXGap(TextPositionSequence currentTextPosition, TextPositionSequence previousTextPosition, double avgTextPositionHeight) {
|
||||
private static boolean isXGap(Word currentTextPosition, Word previousTextPosition, double avgTextPositionHeight) {
|
||||
|
||||
return Math.abs(previousTextPosition.getMaxXDirAdj() - currentTextPosition.getXDirAdj()) > (avgTextPositionHeight * X_GAP_FACTOR);
|
||||
}
|
||||
|
||||
|
||||
private static boolean isSplitByOrientation(TextPositionSequence currentTextPosition, TextPositionSequence previousTextPosition) {
|
||||
private static boolean isSplitByOrientation(Word currentTextPosition, Word previousTextPosition) {
|
||||
|
||||
return !previousTextPosition.getDir().equals(currentTextPosition.getDir());
|
||||
}
|
||||
|
||||
|
||||
private static boolean isNewLine(TextPositionSequence currentTextPosition, TextPositionSequence previousTextPosition, double avgTextPositionHeight) {
|
||||
private static boolean isNewLine(Word currentTextPosition, Word previousTextPosition, double avgTextPositionHeight) {
|
||||
|
||||
return Math.abs(previousTextPosition.getYDirAdj() - currentTextPosition.getYDirAdj()) > avgTextPositionHeight;
|
||||
}
|
||||
@@ -96,13 +96,13 @@ public class LineDetectionService {
|
||||
List<List<Rectangle2D>> bBoxWithGapsByLines;
|
||||
List<Rectangle2D> bBoxWithGapsInCurrentLine;
|
||||
|
||||
List<List<List<TextPositionSequence>>> sequencesWithGapsByLines;
|
||||
List<List<TextPositionSequence>> sequencesWithGapsInCurrentLine;
|
||||
List<List<List<Word>>> sequencesWithGapsByLines;
|
||||
List<List<Word>> sequencesWithGapsInCurrentLine;
|
||||
|
||||
List<TextPositionSequence> currentSequencesWithoutGaps;
|
||||
List<Word> currentSequencesWithoutGaps;
|
||||
|
||||
List<List<TextPositionSequence>> sequencesByLines;
|
||||
List<TextPositionSequence> sequencesInCurrentLine;
|
||||
List<List<Word>> sequencesByLines;
|
||||
List<Word> sequencesInCurrentLine;
|
||||
|
||||
List<List<Rectangle2D>> xGaps;
|
||||
List<List<Rectangle2D>> yGaps;
|
||||
@@ -116,14 +116,14 @@ public class LineDetectionService {
|
||||
List<Rectangle2D> bBoxWithGapsInCurrentLine = new LinkedList<>();
|
||||
bBoxWithGapsByLines.add(bBoxWithGapsInCurrentLine);
|
||||
|
||||
List<List<List<TextPositionSequence>>> sequencesWithGapsByLines = new LinkedList<>();
|
||||
List<List<TextPositionSequence>> sequencesWithGapsInCurrentLine = new LinkedList<>();
|
||||
List<List<List<Word>>> sequencesWithGapsByLines = new LinkedList<>();
|
||||
List<List<Word>> sequencesWithGapsInCurrentLine = new LinkedList<>();
|
||||
sequencesWithGapsByLines.add(sequencesWithGapsInCurrentLine);
|
||||
List<TextPositionSequence> currentSequencesWithoutGaps = new LinkedList<>();
|
||||
List<Word> currentSequencesWithoutGaps = new LinkedList<>();
|
||||
sequencesWithGapsInCurrentLine.add(currentSequencesWithoutGaps);
|
||||
|
||||
List<List<TextPositionSequence>> sequencesByLines = new LinkedList<>();
|
||||
List<TextPositionSequence> sequencesInCurrentLine = new LinkedList<>();
|
||||
List<List<Word>> sequencesByLines = new LinkedList<>();
|
||||
List<Word> sequencesInCurrentLine = new LinkedList<>();
|
||||
sequencesByLines.add(sequencesInCurrentLine);
|
||||
|
||||
return new LineFactory(lineBBox,
|
||||
@@ -178,13 +178,13 @@ public class LineDetectionService {
|
||||
}
|
||||
|
||||
|
||||
private Rectangle2D textPositionBBox(List<TextPositionSequence> textPositionSequences) {
|
||||
private Rectangle2D textPositionBBox(List<Word> words) {
|
||||
|
||||
return RectangleTransformations.rectangle2DBBox(textPositionSequences.stream().map(TextPositionSequence::getBBox).toList());
|
||||
return RectangleTransformations.rectangle2DBBox(words.stream().map(Word::getBBox).toList());
|
||||
}
|
||||
|
||||
|
||||
public void addToCurrentLine(TextPositionSequence current) {
|
||||
public void addToCurrentLine(Word current) {
|
||||
|
||||
sequencesInCurrentLine.add(current);
|
||||
currentSequencesWithoutGaps.add(current);
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.PageContents;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.parsing.PDFLinesTextStripper;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.utils.RectangleTransformations;
|
||||
|
||||
@@ -40,7 +40,7 @@ public class PageContentExtractor {
|
||||
stripper.setPdpage(pdPage);
|
||||
stripper.getText(pdDocument);
|
||||
|
||||
Map<Float, List<TextPositionSequence>> sortedTextPositionSequencesPerDir = stripper.getTextPositionSequences()
|
||||
Map<Float, List<Word>> sortedTextPositionSequencesPerDir = stripper.getWords()
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(textPositionSequence -> textPositionSequence.getDir().getDegrees()));
|
||||
|
||||
@@ -57,7 +57,7 @@ public class PageContentExtractor {
|
||||
}
|
||||
|
||||
|
||||
public List<TextPositionSequence> sortByDirAccordingToPageRotation(Map<Float, List<TextPositionSequence>> sortedTextPositionSequencesPerDir, int rotation) {
|
||||
public List<Word> sortByDirAccordingToPageRotation(Map<Float, List<Word>> sortedTextPositionSequencesPerDir, int rotation) {
|
||||
|
||||
LinkedList<Float> sortedKeys = new LinkedList<>(sortedTextPositionSequencesPerDir.keySet().stream().sorted().toList());
|
||||
|
||||
|
||||
+2
-2
@@ -14,9 +14,9 @@ public class PageInformationService {
|
||||
|
||||
public PageInformation build(PageContents pageContents) {
|
||||
|
||||
LineInformation lineInformation = LineDetectionService.calculateLineInformation(pageContents.getSortedTextPositionSequences());
|
||||
LineInformation lineInformation = LineDetectionService.calculateLineInformation(pageContents.getSortedWords());
|
||||
Rectangle2D mainBodyTextFrame = MainBodyTextFrameExtractionService.calculateMainBodyTextFrame(lineInformation);
|
||||
GapInformation gapInformation = GapDetectionService.findGapsInLines(pageContents.getSortedTextPositionSequences(), mainBodyTextFrame);
|
||||
GapInformation gapInformation = GapDetectionService.findGapsInLines(pageContents.getSortedWords(), mainBodyTextFrame);
|
||||
|
||||
return new PageInformation(pageContents, lineInformation, mainBodyTextFrame, gapInformation);
|
||||
}
|
||||
|
||||
+1
-25
@@ -34,22 +34,7 @@ public class SimplifiedSectionTextService {
|
||||
List<SimplifiedSectionText> simplifiedText = Stream.of(simplifiedMainSectionsList, simplifiedHeadersList, simplifiedFootersList)
|
||||
.flatMap(List::stream)
|
||||
.collect(Collectors.toList());
|
||||
return SimplifiedText.builder()
|
||||
.numberOfPages(document.getNumberOfPages())
|
||||
.sectionTexts(simplifiedText)
|
||||
.mainSectionNumbers(document.getAllSections()
|
||||
.stream()
|
||||
.map(this::getSectionNumber)
|
||||
.toList())
|
||||
.headerSectionNumbers(document.getHeaders()
|
||||
.stream()
|
||||
.map(this::getSectionNumber)
|
||||
.toList())
|
||||
.footerSectionNumbers(document.getFooters()
|
||||
.stream()
|
||||
.map(this::getSectionNumber)
|
||||
.toList())
|
||||
.build();
|
||||
return SimplifiedText.builder().numberOfPages(document.getNumberOfPages()).sectionTexts(simplifiedText).build();
|
||||
}
|
||||
|
||||
|
||||
@@ -64,13 +49,4 @@ public class SimplifiedSectionTextService {
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
private String getSectionNumber(SemanticNode semanticNode) {
|
||||
|
||||
return semanticNode.getTreeId()
|
||||
.stream()
|
||||
.map(String::valueOf)
|
||||
.collect(Collectors.joining("."));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-5
@@ -5,7 +5,7 @@ import java.util.List;
|
||||
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.model.text.TextDirection;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@@ -17,9 +17,9 @@ public class TextRulingsClassifier {
|
||||
private final static double TEXT_BBOX_THRESHOLD_FACTOR = 0.15; // multiplied with text width then subtracted from word width. If ruling covers this width, it is considered as strikethrough/underline.
|
||||
|
||||
|
||||
public static void classifyUnderlinedAndStrikethroughText(List<TextPositionSequence> words, CleanRulings cleanRulings) {
|
||||
public static void classifyUnderlinedAndStrikethroughText(List<Word> words, CleanRulings cleanRulings) {
|
||||
|
||||
for (TextPositionSequence word : words) {
|
||||
for (Word word : words) {
|
||||
if (word.getDir().equals(TextDirection.ZERO) || word.getDir().equals(TextDirection.HALF_CIRCLE)) {
|
||||
handleHorizontalText(cleanRulings, word);
|
||||
} else {
|
||||
@@ -29,7 +29,7 @@ public class TextRulingsClassifier {
|
||||
}
|
||||
|
||||
|
||||
private static void handleVerticalText(CleanRulings cleanRulings, TextPositionSequence word) {
|
||||
private static void handleVerticalText(CleanRulings cleanRulings, Word word) {
|
||||
|
||||
float lowerY = (float) (word.getBBoxPdf().getMinY() + TEXT_BBOX_THRESHOLD_FACTOR * word.getWidth());
|
||||
float upperY = (float) (word.getBBoxPdf().getMaxY() - TEXT_BBOX_THRESHOLD_FACTOR * word.getWidth());
|
||||
@@ -63,7 +63,7 @@ public class TextRulingsClassifier {
|
||||
}
|
||||
|
||||
|
||||
private static void handleHorizontalText(CleanRulings cleanRulings, TextPositionSequence word) {
|
||||
private static void handleHorizontalText(CleanRulings cleanRulings, Word word) {
|
||||
|
||||
float leftX = (float) (word.getBBoxPdf().getMinX() + TEXT_BBOX_THRESHOLD_FACTOR * word.getWidth());
|
||||
float rightX = (float) (word.getBBoxPdf().getMaxX() - TEXT_BBOX_THRESHOLD_FACTOR * word.getWidth());
|
||||
|
||||
+25
-26
@@ -7,17 +7,16 @@ import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.text.similarity.LevenshteinDistance;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngineProto.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationPage;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.PageBlockType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.SectionIdentifier;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.outline.OutlineObject;
|
||||
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.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.utils.TextNormalizationUtilities;
|
||||
|
||||
import lombok.Data;
|
||||
@@ -223,14 +222,14 @@ public class BlockificationPostprocessingService {
|
||||
headline = sectionIdentifier + headline;
|
||||
}
|
||||
|
||||
WordSequenceResult wordSequenceResult = findWordSequence(blockToSplit.getSequences(), headline);
|
||||
WordSequenceResult wordSequenceResult = findWordSequence(blockToSplit.getWords(), headline);
|
||||
if (wordSequenceResult.inSequence.isEmpty() && !headline.equals(title)) {
|
||||
wordSequenceResult = findWordSequence(blockToSplit.getSequences(), title);
|
||||
wordSequenceResult = findWordSequence(blockToSplit.getWords(), title);
|
||||
}
|
||||
|
||||
boolean modifiedBlockToSplit = false;
|
||||
if (!wordSequenceResult.inSequence.isEmpty()) {
|
||||
blockToSplit.setSequences(wordSequenceResult.inSequence);
|
||||
blockToSplit.setWords(wordSequenceResult.inSequence);
|
||||
blockToSplit.recalculateBBox();
|
||||
modifiedBlockToSplit = true;
|
||||
}
|
||||
@@ -251,19 +250,19 @@ public class BlockificationPostprocessingService {
|
||||
}
|
||||
|
||||
|
||||
private static WordSequenceResult findWordSequence(List<TextPositionSequence> textPositionSequences, String text) {
|
||||
private static WordSequenceResult findWordSequence(List<Word> words, String text) {
|
||||
|
||||
String target = sanitizeString(text);
|
||||
List<TextPositionSequence> inSequence = new ArrayList<>();
|
||||
List<TextPositionSequence> preSequence = new ArrayList<>();
|
||||
List<TextPositionSequence> postSequence = new ArrayList<>();
|
||||
List<Word> inSequence = new ArrayList<>();
|
||||
List<Word> preSequence = new ArrayList<>();
|
||||
List<Word> postSequence = new ArrayList<>();
|
||||
StringBuilder currentSequence = new StringBuilder();
|
||||
|
||||
if (target.isBlank()) {
|
||||
return new WordSequenceResult();
|
||||
}
|
||||
|
||||
for (TextPositionSequence sequence : textPositionSequences) {
|
||||
for (Word sequence : words) {
|
||||
|
||||
currentSequence.append(sanitizeString(sequence.toString()));
|
||||
inSequence.add(sequence);
|
||||
@@ -275,10 +274,10 @@ public class BlockificationPostprocessingService {
|
||||
int index = 0;
|
||||
String toRemove = currentSequence.substring(0, currentSequence.length() - target.length());
|
||||
|
||||
TextPositionSequence next = inSequence.get(index);
|
||||
Word next = inSequence.get(index);
|
||||
while (currentSequence.length() - next.length() >= target.length()) {
|
||||
|
||||
TextPositionSequence removed = inSequence.remove(index);
|
||||
Word removed = inSequence.remove(index);
|
||||
currentSequence.delete(0, removed.toString().length());
|
||||
preSequence.add(removed);
|
||||
|
||||
@@ -307,7 +306,7 @@ public class BlockificationPostprocessingService {
|
||||
}
|
||||
|
||||
if (currentSequence.toString().equals(target)) {
|
||||
postSequence.addAll(textPositionSequences.subList(textPositionSequences.indexOf(sequence) + 1, textPositionSequences.size()));
|
||||
postSequence.addAll(words.subList(words.indexOf(sequence) + 1, words.size()));
|
||||
return new WordSequenceResult(inSequence, preSequence, postSequence);
|
||||
}
|
||||
}
|
||||
@@ -317,10 +316,10 @@ public class BlockificationPostprocessingService {
|
||||
}
|
||||
|
||||
|
||||
private static SplitSequenceResult splitSequence(TextPositionSequence sequence, String toRemove) {
|
||||
private static SplitSequenceResult splitSequence(Word sequence, String toRemove) {
|
||||
|
||||
TextPositionSequence in = null;
|
||||
TextPositionSequence out;
|
||||
Word in = null;
|
||||
Word out;
|
||||
|
||||
String currentSequence = sequence.toString().toLowerCase(Locale.ROOT);
|
||||
int index = currentSequence.indexOf(toRemove);
|
||||
@@ -338,9 +337,9 @@ public class BlockificationPostprocessingService {
|
||||
}
|
||||
|
||||
|
||||
private static TextPositionSequence createSubSequence(TextPositionSequence sequence, int start, int end) {
|
||||
private static Word createSubSequence(Word sequence, int start, int end) {
|
||||
|
||||
TextPositionSequence newSeq = new TextPositionSequence(new ArrayList<>(sequence.getTextPositions().subList(start, end)), sequence.getPage());
|
||||
Word newSeq = new Word(new ArrayList<>(sequence.getTextPositions().subList(start, end)), sequence.getPage());
|
||||
newSeq.setParagraphStart(sequence.isParagraphStart());
|
||||
return newSeq;
|
||||
}
|
||||
@@ -355,10 +354,10 @@ public class BlockificationPostprocessingService {
|
||||
List<TextPageBlock> mergedBlocks = new ArrayList<>();
|
||||
for (TextPageBlock textPageBlock : blocksToMerge.subList(1, blocksToMerge.size())) {
|
||||
|
||||
if (firstBlock != null && !firstBlock.getSequences().isEmpty()) {
|
||||
if (firstBlock != null && !firstBlock.getWords().isEmpty()) {
|
||||
|
||||
if (textPageBlock.getDir() == firstBlock.getDir()) {
|
||||
firstBlock.addAll(textPageBlock.getSequences());
|
||||
firstBlock.addAll(textPageBlock.getWords());
|
||||
mergedBlocks.add(textPageBlock);
|
||||
}
|
||||
}
|
||||
@@ -497,12 +496,12 @@ public class BlockificationPostprocessingService {
|
||||
|
||||
public static class WordSequenceResult {
|
||||
|
||||
public List<TextPositionSequence> inSequence;
|
||||
public List<TextPositionSequence> preSequence;
|
||||
public List<TextPositionSequence> postSequence;
|
||||
public List<Word> inSequence;
|
||||
public List<Word> preSequence;
|
||||
public List<Word> postSequence;
|
||||
|
||||
|
||||
public WordSequenceResult(List<TextPositionSequence> inSequence, List<TextPositionSequence> preSequence, List<TextPositionSequence> postSequence) {
|
||||
public WordSequenceResult(List<Word> inSequence, List<Word> preSequence, List<Word> postSequence) {
|
||||
|
||||
this.inSequence = inSequence;
|
||||
this.preSequence = preSequence;
|
||||
@@ -523,7 +522,7 @@ public class BlockificationPostprocessingService {
|
||||
|
||||
}
|
||||
|
||||
public record SplitSequenceResult(TextPositionSequence in, TextPositionSequence out) {
|
||||
public record SplitSequenceResult(Word in, Word out) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -14,7 +14,7 @@ import com.knecon.fforesight.service.layoutparser.processor.model.Classification
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.table.TablePageBlock;
|
||||
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.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.visualization.LayoutDebugLayer;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -30,7 +30,7 @@ public class DocstrumBlockificationService {
|
||||
static final float THRESHOLD = 1f;
|
||||
|
||||
|
||||
public ClassificationPage blockify(List<TextPositionSequence> textPositions,
|
||||
public ClassificationPage blockify(List<Word> textPositions,
|
||||
CleanRulings rulings,
|
||||
boolean xyOrder,
|
||||
LayoutDebugLayer visualizations,
|
||||
@@ -72,16 +72,16 @@ public class DocstrumBlockificationService {
|
||||
List<AbstractPageBlock> abstractPageBlocks = new ArrayList<>();
|
||||
zones.forEach(zone -> {
|
||||
|
||||
List<TextPositionSequence> textPositionSequences = new ArrayList<>();
|
||||
List<Word> words = new ArrayList<>();
|
||||
zone.getLines()
|
||||
.forEach(line -> {
|
||||
line.getWords()
|
||||
.forEach(word -> {
|
||||
textPositionSequences.add(new TextPositionSequence(word.getTextPositions(), word.getPage()));
|
||||
words.add(new Word(word.getTextPositions(), word.getPage()));
|
||||
});
|
||||
});
|
||||
|
||||
abstractPageBlocks.add(buildTextBlock(textPositionSequences, 0));
|
||||
abstractPageBlocks.add(buildTextBlock(words, 0));
|
||||
});
|
||||
|
||||
return abstractPageBlocks;
|
||||
@@ -102,7 +102,7 @@ public class DocstrumBlockificationService {
|
||||
}
|
||||
TextPageBlock current = (TextPageBlock) block;
|
||||
|
||||
if (previous != null && !previous.getSequences().isEmpty()) {
|
||||
if (previous != null && !previous.getWords().isEmpty()) {
|
||||
|
||||
if (current.getDir() != previous.getDir() || usedRulings.lineBetween(current, previous)) {
|
||||
previous = current;
|
||||
@@ -182,8 +182,8 @@ public class DocstrumBlockificationService {
|
||||
|
||||
private TextPageBlock combineBlocksAndResetIterator(TextPageBlock previous, TextPageBlock current, ListIterator<AbstractPageBlock> itty, boolean toDuplicate) {
|
||||
|
||||
previous.addAll(current.getSequences());
|
||||
previous = buildTextBlock(previous.getSequences(), 0);
|
||||
previous.addAll(current.getWords());
|
||||
previous = buildTextBlock(previous.getWords(), 0);
|
||||
previous.setToDuplicate(toDuplicate);
|
||||
if (current.getClassification() != null && previous.getClassification() == null) {
|
||||
previous.setClassification(current.getClassification());
|
||||
@@ -283,8 +283,8 @@ public class DocstrumBlockificationService {
|
||||
if (current.getDir() == inner.getDir() && current.intersects(inner, yThreshold, xThreshold)) {
|
||||
|
||||
boolean toDuplicate = current.isToDuplicate() || inner.isToDuplicate();
|
||||
current.addAll(inner.getSequences());
|
||||
current = buildTextBlock(current.getSequences(), 0);
|
||||
current.addAll(inner.getWords());
|
||||
current = buildTextBlock(current.getWords(), 0);
|
||||
|
||||
current.setToDuplicate(toDuplicate);
|
||||
blocks.set(i, null);
|
||||
@@ -301,7 +301,7 @@ public class DocstrumBlockificationService {
|
||||
}
|
||||
|
||||
|
||||
public static TextPageBlock buildTextBlock(List<TextPositionSequence> wordBlockList, int indexOnPage) {
|
||||
public static TextPageBlock buildTextBlock(List<Word> wordBlockList, int indexOnPage) {
|
||||
|
||||
return new TextPageBlock(wordBlockList);
|
||||
}
|
||||
|
||||
+19
-13
@@ -16,13 +16,14 @@ import com.knecon.fforesight.service.layoutparser.processor.model.Orientation;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.table.TablePageBlock;
|
||||
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.model.text.Word;
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@Service
|
||||
public class DocuMineBlockificationService {
|
||||
|
||||
static final float THRESHOLD = 1f;
|
||||
public static final double FONT_SIZE_CHANGE_RATIO = 0.15;
|
||||
|
||||
Pattern pattern = Pattern.compile("^(\\d{1,2}\\.){1,3}\\d{1,2}\\.?\\s[0-9A-Za-z ()-]{2,50}", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
@@ -36,9 +37,9 @@ public class DocuMineBlockificationService {
|
||||
* @param cleanRulings All rulings on a page
|
||||
* @return Page object that contains the Textblock and text statistics.
|
||||
*/
|
||||
public ClassificationPage blockify(List<TextPositionSequence> textPositions, CleanRulings cleanRulings) {
|
||||
public ClassificationPage blockify(List<Word> textPositions, CleanRulings cleanRulings) {
|
||||
|
||||
List<TextPositionSequence> chunkWords = new ArrayList<>();
|
||||
List<Word> chunkWords = new ArrayList<>();
|
||||
List<AbstractPageBlock> textPageBlocks = new ArrayList<>();
|
||||
|
||||
CleanRulings usedRulings = cleanRulings.withoutTextRulings();
|
||||
@@ -47,11 +48,11 @@ public class DocuMineBlockificationService {
|
||||
double maxX = 0;
|
||||
double minY = 1000;
|
||||
double maxY = 0;
|
||||
TextPositionSequence prev = null;
|
||||
Word prev = null;
|
||||
|
||||
boolean wasSplitted = false;
|
||||
Double splitX1 = null;
|
||||
for (TextPositionSequence word : textPositions) {
|
||||
for (Word word : textPositions) {
|
||||
|
||||
boolean lineSeparation = prev != null && word.getYDirAdj() - prev.getMaxYDirAdj() > Math.min(word.getHeight(), prev.getHeight()) * 1.1;
|
||||
boolean startFromTop = prev != null && word.getYDirAdj() < prev.getYDirAdj() - prev.getTextHeight();
|
||||
@@ -60,11 +61,7 @@ public class DocuMineBlockificationService {
|
||||
boolean newLineAfterSplit = prev != null && word.getYDirAdj() != prev.getYDirAdj() && wasSplitted && splitX1 != word.getXDirAdj();
|
||||
boolean isSplitByRuling = prev != null && usedRulings.lineBetween(prev, word);
|
||||
boolean splitByDir = prev != null && !prev.getDir().equals(word.getDir());
|
||||
boolean splitByOtherFontAndOtherY = prev != null && Math.abs(prev.getMaxYDirAdj() - word.getMaxYDirAdj()) > word.getTextHeight() * 0.2 //
|
||||
&& (word.getFontStyle().contains("bold") && !prev.getFontStyle().contains("bold")
|
||||
|| prev.getFontStyle().contains("bold") && !word.getFontStyle().contains("bold")
|
||||
|| Math.abs(prev.getFontSize() - word.getFontSize()) >= 1
|
||||
|| Math.abs(word.getTextHeight() - prev.getTextHeight()) > 0.8);
|
||||
boolean splitByOtherFontAndOtherY = prev != null && Math.abs(prev.getMaxYDirAdj() - word.getMaxYDirAdj()) > word.getTextHeight() * 0.2 && isFontChange(word, prev);
|
||||
|
||||
Matcher matcher = pattern.matcher(chunkWords.stream()
|
||||
.collect(Collectors.joining(" ")).toString());
|
||||
@@ -127,6 +124,15 @@ public class DocuMineBlockificationService {
|
||||
}
|
||||
|
||||
|
||||
private static boolean isFontChange(Word word, Word prev) {
|
||||
|
||||
return word.getFontStyle().contains("bold") && !prev.getFontStyle().contains("bold")
|
||||
|| prev.getFontStyle().contains("bold") && !word.getFontStyle().contains("bold")
|
||||
|| Math.abs(prev.getFontSize() - word.getFontSize()) >= FONT_SIZE_CHANGE_RATIO * Math.min(prev.getFontSize(), word.getFontSize())
|
||||
|| Math.abs(word.getTextHeight() - prev.getTextHeight()) >= FONT_SIZE_CHANGE_RATIO * Math.min(prev.getTextHeight(), word.getTextHeight());
|
||||
}
|
||||
|
||||
|
||||
public void mergeblocks(ClassificationPage page, CleanRulings usedRulings, float xThreshold, float yThreshold) {
|
||||
|
||||
var blocks = page.getTextBlocks();
|
||||
@@ -169,8 +175,8 @@ public class DocuMineBlockificationService {
|
||||
.equals(inner.getClassification()))) {
|
||||
|
||||
boolean toDuplicate = current.isToDuplicate() || inner.isToDuplicate();
|
||||
current.addAll(inner.getSequences());
|
||||
current = buildTextBlock(current.getSequences(), 0);
|
||||
current.addAll(inner.getWords());
|
||||
current = buildTextBlock(current.getWords(), 0);
|
||||
current.setClassification(inner.getClassification());
|
||||
current.setToDuplicate(toDuplicate);
|
||||
blocks.set(i, null);
|
||||
@@ -193,7 +199,7 @@ public class DocuMineBlockificationService {
|
||||
}
|
||||
|
||||
|
||||
public static TextPageBlock buildTextBlock(List<TextPositionSequence> wordBlockList, int indexOnPage) {
|
||||
public static TextPageBlock buildTextBlock(List<Word> wordBlockList, int indexOnPage) {
|
||||
|
||||
return new TextPageBlock(wordBlockList);
|
||||
}
|
||||
|
||||
+5
-5
@@ -11,7 +11,7 @@ import com.knecon.fforesight.service.layoutparser.processor.model.Classification
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.Orientation;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings;
|
||||
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.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.visualization.LayoutDebugLayer;
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -30,20 +30,20 @@ public class RedactManagerBlockificationService {
|
||||
* @param visualizations
|
||||
* @return Page object that contains the Textblock and text statistics.
|
||||
*/
|
||||
public ClassificationPage blockify(List<TextPositionSequence> textPositions, CleanRulings cleanRulings, LayoutDebugLayer visualizations) {
|
||||
public ClassificationPage blockify(List<Word> textPositions, CleanRulings cleanRulings, LayoutDebugLayer visualizations) {
|
||||
|
||||
CleanRulings usedRulings = cleanRulings.withoutTextRulings();
|
||||
|
||||
int indexOnPage = 0;
|
||||
List<TextPositionSequence> chunkWords = new ArrayList<>();
|
||||
List<Word> chunkWords = new ArrayList<>();
|
||||
List<AbstractPageBlock> chunkBlockList = new ArrayList<>();
|
||||
|
||||
double minX = 1000, maxX = 0, minY = 1000, maxY = 0;
|
||||
TextPositionSequence prev = null;
|
||||
Word prev = null;
|
||||
|
||||
boolean wasSplitted = false;
|
||||
Double splitX1 = null;
|
||||
for (TextPositionSequence word : textPositions) {
|
||||
for (Word word : textPositions) {
|
||||
|
||||
boolean lineSeparation = word.getYDirAdj() - maxY > word.getHeight() * 1.25;
|
||||
boolean startFromTop = prev != null && word.getYDirAdj() < prev.getYDirAdj() - prev.getTextHeight();
|
||||
|
||||
+2
-2
@@ -81,7 +81,7 @@ public class ClarifyndClassificationService {
|
||||
&& (textBlock.getMostPopularWordStyle().equals("bold")
|
||||
|| !document.getFontStyleCounter().getCountPerValue().containsKey("bold")
|
||||
&& textBlock.getMostPopularWordFontSize() > document.getFontSizeCounter().getMostPopular() + 1)
|
||||
&& textBlock.getSequences().get(0).getTextPositions().get(0).getFontSizeInPt() >= textBlock.getMostPopularWordFontSize()) {
|
||||
&& textBlock.getWords().get(0).getTextPositions().get(0).getFontSizeInPt() >= textBlock.getMostPopularWordFontSize()) {
|
||||
|
||||
PageBlockType headlineType = HeadlineClassificationService.headlineClassByFontSize(textBlock, headlineFontSizes);
|
||||
headlineClassificationService.classifyHeadline(textBlock, headlineType);
|
||||
@@ -91,7 +91,7 @@ public class ClarifyndClassificationService {
|
||||
&& textBlock.getMostPopularWordStyle().equals("bold")
|
||||
&& !document.getFontStyleCounter().getMostPopular().equals("bold")
|
||||
&& PositionUtils.getApproxLineCount(textBlock) < 2.9
|
||||
&& textBlock.getSequences().get(0).getTextPositions().get(0).getFontSizeInPt() >= textBlock.getMostPopularWordFontSize()) {
|
||||
&& textBlock.getWords().get(0).getTextPositions().get(0).getFontSizeInPt() >= textBlock.getMostPopularWordFontSize()) {
|
||||
|
||||
PageBlockType headlineType = HeadlineClassificationService.headlineClassByFontSize(textBlock, headlineFontSizes);
|
||||
headlineClassificationService.classifyHeadline(textBlock, headlineType);
|
||||
|
||||
+7
-3
@@ -4,7 +4,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
public class ClassificationPatterns {
|
||||
|
||||
public static final Pattern HEADLINE_WITH_2_IDENTIFER_PATTERN = Pattern.compile("^([1-9]\\d?\\.){1,3}\\d{1,2}\\.?\\s[a-z][0-9a-z \\[\\]]{2,50}", Pattern.CASE_INSENSITIVE);
|
||||
public static final Pattern HEADLINE_WITH_2_IDENTIFER_PATTERN = Pattern.compile("^([1-9]\\d?\\.){1,3}\\d{1,2}\\.?\\s(?:14C)?\\s*[a-z][0-9a-z \\[\\]]{2,50}", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
public static final Pattern HEADLINE_WITH_SINGLE_IDENTIFER_PATTERN = Pattern.compile("^([0-9]\\.)\\s[a-z][0-9a-z \\[\\]]{2,50}", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
@@ -13,15 +13,19 @@ public class ClassificationPatterns {
|
||||
public static final Pattern HEADLINE_PATTERN_WITH_SLASHES = Pattern.compile("^(\\d{1,1}\\.){1,3}\\d{1,2}\\.?\\s[a-z]{1,2}\\/[a-z]{1,2}.*");
|
||||
|
||||
public static final Pattern AMOUNT_PATTERN = Pattern.compile(
|
||||
"^\\s*\\d+(?:\\.\\d+)?\\s*(?:ml|l|g|kg|mg|cm|cm2|cm3|mm|mm2|mm3|km|km2|m|m2|m3|lb|oz|ppm|dpm|days|weeks|months|%|f)\\b",
|
||||
"^\\s*\\d+(?:\\.\\d+)?\\s*(?:ml|ul|μl|l|ug|μg|g|kg|mg|cm|cm2|cm3|mm|mm2|mm3|km|km2|m|m2|m3|lb|oz|ppm|dpm|days|weeks|months|%|f|ppb)\\b",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
|
||||
|
||||
public static final Pattern TABLE_OR_FIGURE_PATTERN = Pattern.compile(
|
||||
public static final Pattern TABLE_OR_FIGURE_HEADLINE_PATTERN = Pattern.compile(
|
||||
"^\\s*(?:table|continued\\s+table|appendix|figure)\\s+(?:[xvi]+|[a-z0-9]{1,3}(?:\\.[0-9]{1,3})*(?:-[0-9]{1,3})?)\\b",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
public static final Pattern TABLE_MID_SENTENCE_PATTERN = Pattern.compile(
|
||||
"(?:table|continued\\s+table|appendix|figure)\\s+(?:[xvi]+|[a-z0-9]{1,3}(?:\\.[0-9]{1,3})*(?:-[0-9]{1,3})?)\\b",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
public static final Pattern ALPHANUMERIC = Pattern.compile("[a-zA-Z0-9]");
|
||||
|
||||
public static final Pattern NUMERIC = Pattern.compile("[0-9]+");
|
||||
|
||||
+16
-3
@@ -6,7 +6,8 @@ import static com.knecon.fforesight.service.layoutparser.processor.services.clas
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.services.classification.ClassificationPatterns.HEADLINE_PATTERN_WITH_SLASHES;
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.services.classification.ClassificationPatterns.HEADLINE_WITH_2_IDENTIFER_PATTERN;
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.services.classification.ClassificationPatterns.HEADLINE_WITH_SINGLE_IDENTIFER_PATTERN;
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.services.classification.ClassificationPatterns.TABLE_OR_FIGURE_PATTERN;
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.services.classification.ClassificationPatterns.TABLE_MID_SENTENCE_PATTERN;
|
||||
import static com.knecon.fforesight.service.layoutparser.processor.services.classification.ClassificationPatterns.TABLE_OR_FIGURE_HEADLINE_PATTERN;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
@@ -15,7 +16,6 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -84,7 +84,8 @@ public class DocuMineClassificationService {
|
||||
Matcher atLeast3Matcher = AT_LEAST_3_CHARS_PATTERN.matcher(textBlock.toString());
|
||||
Matcher headlineWithSlashesMatcher = HEADLINE_PATTERN_WITH_SLASHES.matcher(textBlock.toString());
|
||||
Matcher amountMatcher = AMOUNT_PATTERN.matcher(textBlock.toString());
|
||||
Matcher tableOrFigureMatcher = TABLE_OR_FIGURE_PATTERN.matcher(textBlock.toString());
|
||||
Matcher tableOrFigureMatcher = TABLE_OR_FIGURE_HEADLINE_PATTERN.matcher(textBlock.toString());
|
||||
Matcher tableMidSentenceMatcher = TABLE_MID_SENTENCE_PATTERN.matcher(textBlock.toString());
|
||||
Matcher headlineWithSingleIdentifierMatcher = HEADLINE_WITH_SINGLE_IDENTIFER_PATTERN.matcher(textBlock.toString());
|
||||
boolean isAtLeast3Characters = atLeast3Matcher.reset().find();
|
||||
boolean headlineWithSlashesMatches = headlineWithSlashesMatcher.reset().matches();
|
||||
@@ -149,10 +150,22 @@ public class DocuMineClassificationService {
|
||||
&& greaterOrEqualFontThanPageAverage(textBlock, page)//
|
||||
&& PositionUtils.getApproxLineCount(textBlock) < 2.9//
|
||||
&& (tableOrFigureMatcher.reset().find() || (headlineWithSingleIdentifierMatcher.reset().find() && listIdentifiers.isEmpty())) //
|
||||
&& tableMidSentenceMatcher.reset().results()
|
||||
.count() <= 1 //
|
||||
&& !isAmount//
|
||||
&& !headlineWithSlashesMatches) {
|
||||
|
||||
setAsHeadline(headlineClassificationService, textBlock, document, headlineFontSizes);
|
||||
// } else if (textBlock.getMostPopularWordFont().contains("bold")
|
||||
// && greaterOrEqualFontThanPageAverage(textBlock, page)
|
||||
// && textBlock.getWords().size() <= 6
|
||||
// && PositionUtils.getApproxLineCount(textBlock) < 2.9
|
||||
// && isAtLeast3Characters
|
||||
// && charCount > textBlock.getText().length() * 0.75
|
||||
// && !textBlock.getText().contains(":")
|
||||
// && textBlock.getWidth() < page.getBodyTextFrame().getWidth() * 0.7) {
|
||||
//
|
||||
// setAsHeadline(headlineClassificationService, textBlock, document, headlineFontSizes);
|
||||
} else if (!listIdentifiers.isEmpty()) {
|
||||
|
||||
textBlock.setClassification(PageBlockType.LIST_ITEM);
|
||||
|
||||
+4
-4
@@ -11,7 +11,7 @@ import org.springframework.stereotype.Service;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.AbstractBlockOnPage;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.ListIdentifier;
|
||||
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.model.text.Word;
|
||||
|
||||
@Service
|
||||
public class ListItemClassificationService {
|
||||
@@ -71,7 +71,7 @@ public class ListItemClassificationService {
|
||||
|
||||
List<ListIdentifier> result = new LinkedList<>();
|
||||
if (block.block() instanceof TextPageBlock textBlock) {
|
||||
List<TextPositionSequence> sequences = textBlock.getSequences();
|
||||
List<Word> sequences = textBlock.getWords();
|
||||
for (int i = 0; i < sequences.size(); i++) {
|
||||
|
||||
if (i != 0 && sequences.get(i - 1).getXDirAdj() < sequences.get(i).getXDirAdj()) {
|
||||
@@ -79,8 +79,8 @@ public class ListItemClassificationService {
|
||||
continue;
|
||||
}
|
||||
|
||||
TextPositionSequence sequence = sequences.get(i);
|
||||
List<TextPositionSequence> wordsAtStartOfLine = new ArrayList<>(3);
|
||||
Word sequence = sequences.get(i);
|
||||
List<Word> wordsAtStartOfLine = new ArrayList<>(3);
|
||||
int end = Math.min(sequences.size(), i + 3);
|
||||
for (int j = i; j < end; j++) {
|
||||
if (sequences.get(j).intersectsYDirAdj(sequence, 2)) {
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.services.classification;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
|
||||
public record NumberWord(Word word, int number) {
|
||||
|
||||
}
|
||||
+2
-3
@@ -11,7 +11,6 @@ import com.knecon.fforesight.service.layoutparser.processor.model.Classification
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.PageBlockType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.ImageType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.utils.MarkedContentUtils;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.utils.PositionUtils;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -95,7 +94,7 @@ public class RedactManagerClassificationService {
|
||||
&& (textBlock.getMostPopularWordStyle().equals("bold")
|
||||
|| !document.getFontStyleCounter().getCountPerValue().containsKey("bold")
|
||||
&& textBlock.getMostPopularWordFontSize() > document.getFontSizeCounter().getMostPopular() + 1)
|
||||
&& textBlock.getSequences().get(0).getTextPositions().get(0).getFontSizeInPt() >= textBlock.getMostPopularWordFontSize()) {
|
||||
&& textBlock.getWords().get(0).getTextPositions().get(0).getFontSizeInPt() >= textBlock.getMostPopularWordFontSize()) {
|
||||
|
||||
PageBlockType headlineType = HeadlineClassificationService.headlineClassByFontSize(textBlock, headlineFontSizes);
|
||||
headlineClassificationService.classifyHeadline(textBlock, headlineType);
|
||||
@@ -105,7 +104,7 @@ public class RedactManagerClassificationService {
|
||||
&& textBlock.getMostPopularWordStyle().equals("bold")
|
||||
&& !document.getFontStyleCounter().getMostPopular().equals("bold")
|
||||
&& PositionUtils.getApproxLineCount(textBlock) < 2.9
|
||||
&& textBlock.getSequences().get(0).getTextPositions().get(0).getFontSizeInPt() >= textBlock.getMostPopularWordFontSize()) {
|
||||
&& textBlock.getWords().get(0).getTextPositions().get(0).getFontSizeInPt() >= textBlock.getMostPopularWordFontSize()) {
|
||||
|
||||
PageBlockType headlineType = HeadlineClassificationService.headlineClassByFontSize(textBlock, headlineFontSizes);
|
||||
headlineClassificationService.classifyHeadline(textBlock, headlineType);
|
||||
|
||||
+135
-78
@@ -14,6 +14,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -23,10 +24,11 @@ import com.knecon.fforesight.service.layoutparser.processor.model.AbstractPageBl
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationDocument;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationPage;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.PageBlockType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.SectionIdentifier;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextBlockOnPage;
|
||||
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.model.text.TextPositionSequenceComparator;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TocNumberComparator;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.utils.TextNormalizationUtilities;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.visualization.LayoutDebugLayer;
|
||||
|
||||
@@ -69,11 +71,14 @@ public class TableOfContentsClassificationService {
|
||||
|
||||
private int identifyTOCItems(int start, List<TextBlockOnPage> textBlocks, ClassificationDocument document) {
|
||||
|
||||
if (start >= textBlocks.size()) {
|
||||
return start;
|
||||
}
|
||||
ClassificationPage startPage = textBlocks.get(start).page();
|
||||
List<TextBlockOnPage> initialLookAhead = textBlocks.subList(start, Math.min(start + SURROUNDING_BLOCKS_RADIUS, textBlocks.size()));
|
||||
HashMap<TextPositionSequence, TextBlockOnPage> lookup = new HashMap<>();
|
||||
List<TextPositionSequence> numbers = extractNumbers(initialLookAhead, lookup, document.getPages().size());
|
||||
TocNumberFinder tocNumberFinder = new TocNumberFinder(numbers, lookup);
|
||||
HashMap<NumberWord, TextBlockOnPage> numberToBlockLookup = new HashMap<>();
|
||||
List<NumberWord> numbers = extractNumbers(initialLookAhead, numberToBlockLookup, document.getPages().size());
|
||||
TocNumberFinder tocNumberFinder = new TocNumberFinder(numbers, numberToBlockLookup);
|
||||
|
||||
int lastCandidate = start;
|
||||
for (int i = start; i < Math.min(lastCandidate + SURROUNDING_BLOCKS_RADIUS, textBlocks.size()); i++) {
|
||||
@@ -93,28 +98,28 @@ public class TableOfContentsClassificationService {
|
||||
break;
|
||||
}
|
||||
|
||||
List<TextPositionSequence> numbersFromBlock = extractNumbers(textBlockOnPage, lookup, document.getPages().size());
|
||||
List<NumberWord> numbersFromBlock = extractNumbers(textBlockOnPage, numberToBlockLookup, document.getPages().size());
|
||||
|
||||
List<TextPositionSequence> currentRightmostCluster = tocNumberFinder.getCurrentRightmostCluster();
|
||||
List<NumberWord> currentRightmostCluster = tocNumberFinder.getCurrentRightmostCluster();
|
||||
|
||||
if (currentRightmostCluster.size() < MINIMUM_MATCHES) {
|
||||
log.debug("No numbers indicating a table of contents here.");
|
||||
return start;
|
||||
}
|
||||
|
||||
if (anyIntersection(currentRightmostCluster, numbersFromBlock, lookup)) {
|
||||
if (anyIntersection(currentRightmostCluster, numbersFromBlock, numberToBlockLookup)) {
|
||||
lastCandidate = i;
|
||||
numbersFromBlock.forEach(tocNumberFinder::add);
|
||||
}
|
||||
}
|
||||
|
||||
addVisualization(document.getLayoutDebugLayer(), tocNumberFinder, lookup);
|
||||
|
||||
Set<TextBlockOnPage> blocksWithNumberInCluster = tocNumberFinder.getCurrentRightmostCluster()
|
||||
.stream()
|
||||
.map(lookup::get)
|
||||
.map(numberToBlockLookup::get)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
addVisualization(document.getLayoutDebugLayer(), tocNumberFinder, numberToBlockLookup, blocksWithNumberInCluster, textBlocks.get(start - 1));
|
||||
|
||||
int lastConfirmed = start;
|
||||
for (int i = start; i < lastCandidate + 1; i++) {
|
||||
TextBlockOnPage textBlockOnPage = textBlocks.get(i);
|
||||
@@ -132,18 +137,22 @@ public class TableOfContentsClassificationService {
|
||||
}
|
||||
|
||||
|
||||
private static void addVisualization(LayoutDebugLayer layoutDebugLayer, TocNumberFinder tocNumberFinder, Map<TextPositionSequence, TextBlockOnPage> lookup) {
|
||||
private static void addVisualization(LayoutDebugLayer layoutDebugLayer,
|
||||
TocNumberFinder tocNumberFinder,
|
||||
Map<NumberWord, TextBlockOnPage> lookup,
|
||||
Set<TextBlockOnPage> blocksWithNumberInCluster,
|
||||
TextBlockOnPage startingHeadline) {
|
||||
|
||||
tocNumberFinder.getCurrentRightmostCluster()
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(key -> lookup.get(key).page().getPageNumber()))
|
||||
.forEach((pageNumber, number) -> layoutDebugLayer.addTocPages(number, pageNumber));
|
||||
layoutDebugLayer.addTocBlocks(blocksWithNumberInCluster);
|
||||
layoutDebugLayer.addTocBlocks(Set.of(startingHeadline));
|
||||
}
|
||||
|
||||
|
||||
private static boolean anyIntersection(Collection<TextPositionSequence> numbers1,
|
||||
Collection<TextPositionSequence> numbers2,
|
||||
Map<TextPositionSequence, TextBlockOnPage> lookup) {
|
||||
private static boolean anyIntersection(Collection<NumberWord> numbers1, Collection<NumberWord> numbers2, Map<NumberWord, TextBlockOnPage> lookup) {
|
||||
|
||||
return numbers1.stream()
|
||||
.anyMatch(numberFromCluster -> numbers2.stream()
|
||||
@@ -151,9 +160,9 @@ public class TableOfContentsClassificationService {
|
||||
}
|
||||
|
||||
|
||||
private static List<TextPositionSequence> extractNumbers(List<TextBlockOnPage> textBlocks, Map<TextPositionSequence, TextBlockOnPage> lookup, int numberOfPages) {
|
||||
private static List<NumberWord> extractNumbers(List<TextBlockOnPage> textBlocks, Map<NumberWord, TextBlockOnPage> lookup, int numberOfPages) {
|
||||
|
||||
List<TextPositionSequence> blocks = new LinkedList<>();
|
||||
List<NumberWord> blocks = new LinkedList<>();
|
||||
for (TextBlockOnPage textBlock : textBlocks) {
|
||||
blocks.addAll(extractNumbers(textBlock, lookup, numberOfPages));
|
||||
}
|
||||
@@ -161,30 +170,40 @@ public class TableOfContentsClassificationService {
|
||||
}
|
||||
|
||||
|
||||
private static List<TextPositionSequence> extractNumbers(TextBlockOnPage textBlock, Map<TextPositionSequence, TextBlockOnPage> lookup, int numberOfPages) {
|
||||
private static List<NumberWord> extractNumbers(TextBlockOnPage textBlock, Map<NumberWord, TextBlockOnPage> lookup, int numberOfPages) {
|
||||
|
||||
List<TextPositionSequence> blocks = new LinkedList<>();
|
||||
List<NumberWord> blocks = new LinkedList<>();
|
||||
TextPageBlock block = textBlock.textBlock();
|
||||
List<TextPositionSequence> sequences = block.getSequences();
|
||||
for (int i = 0; i < sequences.size(); i++) {
|
||||
List<Word> words = block.getWords();
|
||||
for (int i = 0; i < words.size(); i++) {
|
||||
|
||||
TextPositionSequence word = sequences.get(i);
|
||||
|
||||
if (!NUMERIC.matcher(word).matches()) {
|
||||
Word word = words.get(i);
|
||||
if (!wordIsEndOfLine(i, words)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AMOUNT_PATTERN.matcher(getSurroundingString(i, sequences)).matches()) {
|
||||
if (AMOUNT_PATTERN.matcher(getSurroundingString(i, words)).matches()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Matcher matcher = SectionIdentifier.numericalIdentifierPattern.matcher(word.toString());
|
||||
if (matcher.find() && matcher.group(2) != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Matcher numberFinder = NUMERIC.matcher(word);
|
||||
if (!numberFinder.find() || word.length() > 5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
int pageNumber = Integer.parseInt(word.toString());
|
||||
int pageNumber = Integer.parseInt(numberFinder.group());
|
||||
if (0 >= pageNumber || pageNumber > numberOfPages) {
|
||||
continue;
|
||||
}
|
||||
lookup.put(word, textBlock);
|
||||
blocks.add(word);
|
||||
NumberWord numberWord = new NumberWord(word, pageNumber);
|
||||
lookup.put(numberWord, textBlock);
|
||||
blocks.add(numberWord);
|
||||
} catch (NumberFormatException e) {
|
||||
log.debug("That wasn't a number! Should not happen, due to numeric check beforehand.");
|
||||
}
|
||||
@@ -193,23 +212,34 @@ public class TableOfContentsClassificationService {
|
||||
}
|
||||
|
||||
|
||||
private static CharSequence getSurroundingString(int i, List<TextPositionSequence> sequences) {
|
||||
private static boolean wordIsEndOfLine(int i, List<Word> words) {
|
||||
|
||||
if (i == words.size() - 1) {
|
||||
return true;
|
||||
}
|
||||
Word word = words.get(i);
|
||||
Word nextWord = words.get(i + 1);
|
||||
return !nextWord.rightOf(word);
|
||||
}
|
||||
|
||||
|
||||
private static CharSequence getSurroundingString(int i, List<Word> sequences) {
|
||||
|
||||
int end = Math.min(i + 5, sequences.size());
|
||||
return sequences.subList(i, end)
|
||||
.stream()
|
||||
.map(TextPositionSequence::toString)
|
||||
.map(Word::toString)
|
||||
.collect(Collectors.joining(" "));
|
||||
}
|
||||
|
||||
|
||||
private static boolean matches(TextPositionSequence number1, TextPositionSequence number2, Map<TextPositionSequence, TextBlockOnPage> lookup) {
|
||||
private static boolean matches(NumberWord number1, NumberWord number2, Map<NumberWord, TextBlockOnPage> lookup) {
|
||||
|
||||
if (number1.getDir() != number2.getDir()) {
|
||||
if (number1.word().getDir() != number2.word().getDir()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return number1.intersectsXDirAdj(number2, INTERSECTION_TOLERANCE);
|
||||
return number1.word().intersectsXDirAdj(number2.word(), INTERSECTION_TOLERANCE);
|
||||
}
|
||||
|
||||
|
||||
@@ -247,11 +277,11 @@ public class TableOfContentsClassificationService {
|
||||
|
||||
private static class TocNumberFinder {
|
||||
|
||||
final UnionFind<TextPositionSequence> numberClusters;
|
||||
final HashMap<TextPositionSequence, TextBlockOnPage> lookup;
|
||||
final UnionFind<NumberWord> numberClusters;
|
||||
final HashMap<NumberWord, TextBlockOnPage> lookup;
|
||||
|
||||
|
||||
TocNumberFinder(List<TextPositionSequence> blocks, HashMap<TextPositionSequence, TextBlockOnPage> lookup) {
|
||||
TocNumberFinder(List<NumberWord> blocks, HashMap<NumberWord, TextBlockOnPage> lookup) {
|
||||
|
||||
this.numberClusters = new UnionFind<>(new HashSet<>(blocks));
|
||||
for (int i = 0; i < blocks.size(); i++) {
|
||||
@@ -265,14 +295,14 @@ public class TableOfContentsClassificationService {
|
||||
}
|
||||
|
||||
|
||||
public void add(TextPositionSequence number) {
|
||||
public void add(NumberWord number) {
|
||||
|
||||
if (numberClusters.getElements().contains(number)) {
|
||||
return;
|
||||
}
|
||||
|
||||
numberClusters.addElement(number);
|
||||
for (TextPositionSequence element : numberClusters.getElements()) {
|
||||
for (NumberWord element : numberClusters.getElements()) {
|
||||
if (matches(number, element, lookup)) {
|
||||
numberClusters.union(element, number);
|
||||
}
|
||||
@@ -280,73 +310,100 @@ public class TableOfContentsClassificationService {
|
||||
}
|
||||
|
||||
|
||||
public List<TextPositionSequence> getCurrentRightmostCluster() {
|
||||
public List<NumberWord> getCurrentRightmostCluster() {
|
||||
|
||||
return numberClusters.getGroups()
|
||||
.stream()
|
||||
.filter(cluster -> cluster.size() > MINIMUM_MATCHES)
|
||||
.map(cluster -> cluster.stream()
|
||||
.sorted(new TextPositionSequenceComparator(lookup))
|
||||
.sorted(new TocNumberComparator(lookup))
|
||||
.toList())
|
||||
.map(this::removeOutliers)
|
||||
// .map(this::filterByMinimumDensity)
|
||||
.map(this::removeOnNonConsecutivePages)
|
||||
.map(this::filterByWordNearTopOfPage)
|
||||
.filter(cluster -> cluster.size() > MINIMUM_MATCHES)
|
||||
.max(Comparator.comparingDouble(cluster -> cluster.get(0).getBBox().getMaxX())).orElse(Collections.emptyList());
|
||||
.max(Comparator.comparingDouble(cluster -> cluster.get(0).word().getBBox().getMaxX())).orElse(Collections.emptyList());
|
||||
}
|
||||
|
||||
// does not seem to be doing much, ideally instead of using the height of the blocks i would like to use the height, beginning from the MainBody top,
|
||||
// but as the MainBody is often wrong, this results in some numbers being discarded even though they are correct.
|
||||
// private List<TextPositionSequence> filterByMinimumDensity(List<TextPositionSequence> numbers) {
|
||||
//
|
||||
// Map<ClassificationPage, List<TextPositionSequence>> clustersPerPage = numbers.stream()
|
||||
// .collect(Collectors.groupingBy(number -> lookup.get(number).page()));
|
||||
//
|
||||
// List<TextPositionSequence> result = new ArrayList<>(numbers.size());
|
||||
// clustersPerPage.keySet()
|
||||
// .stream()
|
||||
// .sorted(Comparator.comparingInt(ClassificationPage::getPageNumber))
|
||||
// .forEach(page -> {
|
||||
// var numbersOnPage = clustersPerPage.get(page);
|
||||
//
|
||||
// double height = numbersOnPage.stream()
|
||||
// .map(BoundingBox::getBBox)
|
||||
// .collect(RectangleTransformations.collectBBox()).getHeight();
|
||||
//
|
||||
// double count = numbersOnPage.size();
|
||||
//
|
||||
// if ((count / height) >= (DENSITY_THRESHOLD_COUNT / page.getPageHeight())) {
|
||||
// result.addAll(numbers);
|
||||
// }
|
||||
// });
|
||||
// return result;
|
||||
// }
|
||||
|
||||
private List<NumberWord> removeOnNonConsecutivePages(List<NumberWord> numbers) {
|
||||
|
||||
public List<TextPositionSequence> removeOutliers(List<TextPositionSequence> numbers) {
|
||||
|
||||
List<TextPositionSequence> result = new ArrayList<>();
|
||||
List<NumberWord> result = new ArrayList<>();
|
||||
|
||||
result.add(numbers.get(0));
|
||||
|
||||
for (int i = 1; i < numbers.size(); i++) {
|
||||
int prev = getPageNumber(numbers, i - 1);
|
||||
int curr = getPageNumber(numbers, i);
|
||||
|
||||
if (Math.abs(prev - curr) > 1) {
|
||||
break;
|
||||
} else {
|
||||
result.add(numbers.get(i));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private int getPageNumber(List<NumberWord> numbers, int i) {
|
||||
|
||||
return lookup.get(numbers.get(i)).page().getPageNumber();
|
||||
}
|
||||
|
||||
|
||||
private List<NumberWord> filterByWordNearTopOfPage(List<NumberWord> numbers) {
|
||||
|
||||
List<NumberWord> result = new ArrayList<>();
|
||||
|
||||
result.add(numbers.get(0));
|
||||
|
||||
for (int i = 1; i < numbers.size(); i++) {
|
||||
NumberWord prev = numbers.get(i - 1);
|
||||
NumberWord curr = numbers.get(i);
|
||||
ClassificationPage prevPage = lookup.get(prev).page();
|
||||
ClassificationPage currPage = lookup.get(curr).page();
|
||||
if (prevPage.equals(currPage)) {
|
||||
result.add(curr);
|
||||
} else if (curr.word().getBBox().getMinY() < currPage.getPageHeight() * 0.33) {
|
||||
result.add(curr);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public List<NumberWord> removeOutliers(List<NumberWord> numbers) {
|
||||
|
||||
List<NumberWord> confirmedClusterNumbers = new ArrayList<>();
|
||||
|
||||
confirmedClusterNumbers.add(numbers.get(0));
|
||||
|
||||
for (int i = 1; i < numbers.size() - 1; i++) {
|
||||
int prev = getNumberAsInt(numbers, i - 1);
|
||||
int curr = getNumberAsInt(numbers, i);
|
||||
int next = getNumberAsInt(numbers, i + 1);
|
||||
|
||||
if (!(curr <= prev || curr >= next) || !isBetterWithout(numbers, i)) {
|
||||
result.add(numbers.get(i));
|
||||
confirmedClusterNumbers.add(numbers.get(i));
|
||||
}
|
||||
}
|
||||
if (getNumberAsInt(numbers, numbers.size() - 1) >= getNumberAsInt(numbers, Math.max(0, numbers.size() - 2))) {
|
||||
result.add(numbers.get(numbers.size() - 1));
|
||||
if (getNumberAsInt(numbers, numbers.size() - 1) >= getLatestNumber(confirmedClusterNumbers)) {
|
||||
confirmedClusterNumbers.add(numbers.get(numbers.size() - 1));
|
||||
}
|
||||
|
||||
return result;
|
||||
return confirmedClusterNumbers;
|
||||
}
|
||||
|
||||
|
||||
private static int getLatestNumber(List<NumberWord> confirmedClusterNumbers) {
|
||||
|
||||
return confirmedClusterNumbers.get(confirmedClusterNumbers.size() - 1).number();
|
||||
}
|
||||
|
||||
|
||||
// Helper method to check if removing the current number results in a better order
|
||||
public static boolean isBetterWithout(List<TextPositionSequence> numbers, int i) {
|
||||
public static boolean isBetterWithout(List<NumberWord> numbers, int i) {
|
||||
|
||||
if (i == 0 || i == numbers.size() - 1) {
|
||||
return false;
|
||||
@@ -362,9 +419,9 @@ public class TableOfContentsClassificationService {
|
||||
}
|
||||
|
||||
|
||||
private static int getNumberAsInt(List<TextPositionSequence> numbers, int i) {
|
||||
private static int getNumberAsInt(List<NumberWord> numbers, int i) {
|
||||
|
||||
return Integer.parseInt(numbers.get(i).toString());
|
||||
return numbers.get(i).number();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-11
@@ -65,7 +65,7 @@ public class DocumentGraphFactory {
|
||||
|
||||
document.getPages()
|
||||
.forEach(context::buildAndAddPageWithCounter);
|
||||
addSectionsForToC(layoutParsingType, document, context, documentGraph);
|
||||
addSections(layoutParsingType, document, context, documentGraph);
|
||||
addHeaderAndFooterToEachPage(document, context);
|
||||
|
||||
documentGraph.setNumberOfPages(context.pages.size());
|
||||
@@ -92,17 +92,17 @@ public class DocumentGraphFactory {
|
||||
}
|
||||
|
||||
|
||||
private void addSectionsForToC(LayoutParsingType layoutParsingType, ClassificationDocument classificationDocument, Context context, Document document) {
|
||||
private void addSections(LayoutParsingType layoutParsingType, ClassificationDocument classificationDocument, Context context, Document document) {
|
||||
|
||||
for (TableOfContentItem tocItem : classificationDocument.getTableOfContents()) {
|
||||
GenericSemanticNode parent = tocItem.getParent() == null ? null : tocItem.getParent().getSection();
|
||||
Optional<GenericSemanticNode> section = SectionNodeFactory.addSection(layoutParsingType,
|
||||
parent,
|
||||
tocItem.getChildren().isEmpty(),
|
||||
tocItem.getNonEmptySectionBlocks(),
|
||||
tocItem.getImages(),
|
||||
context,
|
||||
document);
|
||||
parent,
|
||||
tocItem.getChildren().isEmpty(),
|
||||
tocItem.getNonEmptySectionBlocks(),
|
||||
tocItem.getImages(),
|
||||
context,
|
||||
document);
|
||||
tocItem.setSection(section.orElse(null));
|
||||
}
|
||||
}
|
||||
@@ -133,9 +133,9 @@ public class DocumentGraphFactory {
|
||||
|
||||
if (node instanceof DuplicatedParagraph duplicatedParagraph) {
|
||||
AtomicTextBlock unsortedTextBlock = context.textBlockFactory.buildAtomicTextBlock(textBlocks.stream()
|
||||
.flatMap(tb -> tb.getSequences()
|
||||
.stream())
|
||||
.collect(Collectors.toList()), node, context, page);
|
||||
.flatMap(tb -> tb.getWords()
|
||||
.stream())
|
||||
.collect(Collectors.toList()), node, context, page);
|
||||
duplicatedParagraph.setUnsortedLeafTextBlock(unsortedTextBlock);
|
||||
}
|
||||
|
||||
@@ -260,6 +260,7 @@ public class DocumentGraphFactory {
|
||||
DocumentTree documentTree;
|
||||
Map<Page, Integer> pages;
|
||||
List<AbstractSemanticNode> sections;
|
||||
List<ClassifiedImage> images;
|
||||
TextBlockFactory textBlockFactory;
|
||||
|
||||
|
||||
@@ -268,6 +269,7 @@ public class DocumentGraphFactory {
|
||||
documentTree = new DocumentTree(document);
|
||||
pages = new HashMap<>();
|
||||
sections = new LinkedList<>();
|
||||
images = new LinkedList<>();
|
||||
textBlockFactory = new TextBlockFactory();
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -12,7 +12,7 @@ import java.util.Objects;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.TextRange;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.RedTextPosition;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@@ -28,7 +28,7 @@ public class SearchTextWithTextPositionFactory {
|
||||
public static final double LINEBREAK_DELTA_TOLERANCE = 1.5;
|
||||
|
||||
|
||||
public SearchTextWithTextPositionDto buildSearchTextToTextPositionDto(List<TextPositionSequence> sequences) {
|
||||
public SearchTextWithTextPositionDto buildSearchTextToTextPositionDto(List<Word> sequences) {
|
||||
|
||||
if (sequences.isEmpty() || sequences.stream()
|
||||
.allMatch(sequence -> sequence.getTextPositions().isEmpty())) {
|
||||
@@ -40,7 +40,7 @@ public class SearchTextWithTextPositionFactory {
|
||||
RedTextPosition currentTextPosition = sequences.get(0).getTextPositions().get(0);
|
||||
RedTextPosition previousTextPosition = RedTextPosition.builder().unicode(" ").bBoxDirAdj(currentTextPosition.getBBoxDirAdj()).build();
|
||||
|
||||
for (TextPositionSequence word : sequences) {
|
||||
for (Word word : sequences) {
|
||||
for (int i = 0; i < word.getTextPositions().size(); ++i) {
|
||||
|
||||
currentTextPosition = word.getTextPositions().get(i);
|
||||
@@ -66,7 +66,7 @@ public class SearchTextWithTextPositionFactory {
|
||||
}
|
||||
|
||||
List<Rectangle2D> positions = sequences.stream()
|
||||
.map(TextPositionSequence::getTextPositions)
|
||||
.map(Word::getTextPositions)
|
||||
.flatMap(Collection::stream)
|
||||
.map(RedTextPosition::getBBoxPdf)
|
||||
.toList();
|
||||
|
||||
+3
-3
@@ -16,7 +16,7 @@ import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Ta
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.TextBlock;
|
||||
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.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.utils.TextPositionOperations;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
@@ -115,7 +115,7 @@ public class TableNodeFactory {
|
||||
if (cell.getTextBlocks().isEmpty()) {
|
||||
tableCell.setLeafTextBlock(context.getTextBlockFactory().emptyTextBlock(tableNode, context, page));
|
||||
} else if (cell.getTextBlocks().size() == 1) {
|
||||
textBlock = context.getTextBlockFactory().buildAtomicTextBlock(cell.getTextBlocks().get(0).getSequences(), tableCell, context, page);
|
||||
textBlock = context.getTextBlockFactory().buildAtomicTextBlock(cell.getTextBlocks().get(0).getWords(), tableCell, context, page);
|
||||
tableCell.setLeafTextBlock(textBlock);
|
||||
} else if (firstTextBlockIsHeadline(cell)) {
|
||||
SectionNodeFactory.addSection(layoutParsingType,
|
||||
@@ -129,7 +129,7 @@ public class TableNodeFactory {
|
||||
context,
|
||||
document);
|
||||
} else if (cellAreaIsSmallerThanPageAreaTimesThreshold(cell, page)) {
|
||||
List<TextPositionSequence> sequences = TextPositionOperations.mergeAndSort(cell.getTextBlocks());
|
||||
List<Word> sequences = TextPositionOperations.mergeAndSort(cell.getTextBlocks());
|
||||
textBlock = context.getTextBlockFactory().buildAtomicTextBlock(sequences, tableCell, context, page);
|
||||
tableCell.setLeafTextBlock(textBlock);
|
||||
} else {
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@ package com.knecon.fforesight.service.layoutparser.processor.services.factory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Page;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.SemanticNode;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.AtomicTextBlock;
|
||||
@@ -17,14 +17,14 @@ public class TextBlockFactory {
|
||||
long textBlockIdx;
|
||||
|
||||
|
||||
public AtomicTextBlock buildAtomicTextBlock(List<TextPositionSequence> sequences, SemanticNode parent, DocumentGraphFactory.Context context, Page page) {
|
||||
public AtomicTextBlock buildAtomicTextBlock(List<Word> sequences, SemanticNode parent, DocumentGraphFactory.Context context, Page page) {
|
||||
|
||||
Integer numberOnPage = context.getAndIncrementTextBlockNumberOnPage(page);
|
||||
return buildAtomicTextBlock(sequences, parent, numberOnPage, page);
|
||||
}
|
||||
|
||||
|
||||
public AtomicTextBlock buildAtomicTextBlock(List<TextPositionSequence> sequences, SemanticNode parent, Integer numberOnPage, Page page) {
|
||||
public AtomicTextBlock buildAtomicTextBlock(List<Word> sequences, SemanticNode parent, Integer numberOnPage, Page page) {
|
||||
|
||||
SearchTextWithTextPositionDto searchTextWithTextPositionDto = SearchTextWithTextPositionFactory.buildSearchTextToTextPositionDto(sequences);
|
||||
int offset = stringOffset;
|
||||
|
||||
+5
-5
@@ -11,7 +11,7 @@ import org.springframework.stereotype.Service;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.docstrum.model.BoundingBox;
|
||||
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.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.utils.PageInformation;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -33,10 +33,10 @@ public class GraphicExtractorService {
|
||||
PDPage pdPage,
|
||||
int pageNumber,
|
||||
CleanRulings cleanRulings,
|
||||
List<TextPositionSequence> textPositionSequences,
|
||||
List<Word> words,
|
||||
boolean graphicsRaster) {
|
||||
|
||||
List<Box> characterBBoxes = getCharacterBBoxes(textPositionSequences);
|
||||
List<Box> characterBBoxes = getCharacterBBoxes(words);
|
||||
List<Box> classifiedRulingsBoxes = getLineBBoxesOfAllClassifiedRulings(cleanRulings);
|
||||
|
||||
GraphicBBDetector graphicBBDetector = new GraphicBBDetector(pdPage, true);
|
||||
@@ -63,9 +63,9 @@ public class GraphicExtractorService {
|
||||
}
|
||||
|
||||
|
||||
private List<Box> getCharacterBBoxes(List<TextPositionSequence> textPositionSequences) {
|
||||
private List<Box> getCharacterBBoxes(List<Word> words) {
|
||||
|
||||
return textPositionSequences.stream()
|
||||
return words.stream()
|
||||
.map(BoundingBox::getBBoxPdf)
|
||||
.map(Box::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
+56
-81
@@ -1,10 +1,6 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.services.mapper;
|
||||
|
||||
import static com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentTextDataProto.AllDocumentTextData;
|
||||
import static com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentTextDataProto.DocumentTextData;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -13,15 +9,11 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPageProto.AllDocumentPages;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPageProto.DocumentPage;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPositionDataProto.AllDocumentPositionData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPositionDataProto.DocumentPositionData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPositionDataProto.DocumentPositionData.Position;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructureProto.DocumentStructure;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructureWrapper;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.EntryDataProto.EntryData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngineProto.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPage;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPositionData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructure;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentTextData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.DocumentTree;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.DuplicatedParagraph;
|
||||
@@ -40,57 +32,44 @@ public class DocumentDataMapper {
|
||||
public DocumentData toDocumentData(Document document) {
|
||||
|
||||
List<DocumentTextData> documentTextData = document.streamTerminalTextBlocksInOrder()
|
||||
.flatMap(textBlock -> textBlock.getAtomicTextBlocks()
|
||||
.stream())
|
||||
.flatMap(textBlock -> textBlock.getAtomicTextBlocks().stream())
|
||||
.distinct()
|
||||
.map(DocumentDataMapper::toAtomicTextBlockData)
|
||||
.toList();
|
||||
|
||||
AllDocumentTextData allDocumentTextData = AllDocumentTextData.newBuilder().addAllDocumentTextData(documentTextData).build();
|
||||
|
||||
List<DocumentPositionData> atomicPositionBlockData = document.streamTerminalTextBlocksInOrder()
|
||||
.flatMap(textBlock -> textBlock.getAtomicTextBlocks()
|
||||
.stream())
|
||||
.flatMap(textBlock -> textBlock.getAtomicTextBlocks().stream())
|
||||
.distinct()
|
||||
.map(DocumentDataMapper::toAtomicPositionBlockData)
|
||||
.toList();
|
||||
|
||||
AllDocumentPositionData allDocumentPositionData = AllDocumentPositionData.newBuilder().addAllDocumentPositionData(atomicPositionBlockData).build();
|
||||
Set<Long> nonEmptyTextBlocks = documentTextData.stream().mapToLong(DocumentTextData::getId).boxed().collect(Collectors.toSet());
|
||||
|
||||
Set<Long> nonEmptyTextBlocks = documentTextData.stream()
|
||||
.mapToLong(DocumentTextData::getId).boxed()
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<DocumentPage> documentPageData = document.getPages()
|
||||
.stream()
|
||||
.map(DocumentDataMapper::toPageData)
|
||||
.toList();
|
||||
|
||||
AllDocumentPages allDocumentPages = AllDocumentPages.newBuilder().addAllDocumentPages(documentPageData).build();
|
||||
DocumentStructureWrapper tableOfContentsData = toDocumentTreeData(document.getDocumentTree());
|
||||
List<DocumentPage> documentPageData = document.getPages().stream().map(DocumentDataMapper::toPageData).toList();
|
||||
DocumentStructure tableOfContentsData = toDocumentTreeData(document.getDocumentTree());
|
||||
return DocumentData.builder()
|
||||
.documentTextData(allDocumentTextData)
|
||||
.documentPositions(allDocumentPositionData)
|
||||
.documentPages(allDocumentPages)
|
||||
.documentStructureWrapper(tableOfContentsData)
|
||||
.documentTextData(documentTextData.toArray(new DocumentTextData[0]))
|
||||
.documentPositions(atomicPositionBlockData.toArray(new DocumentPositionData[0]))
|
||||
.documentPages(documentPageData.toArray(new DocumentPage[0]))
|
||||
.documentStructure(tableOfContentsData)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
private DocumentStructureWrapper toDocumentTreeData(DocumentTree documentTree) {
|
||||
private DocumentStructure toDocumentTreeData(DocumentTree documentTree) {
|
||||
|
||||
return new DocumentStructureWrapper(DocumentStructure.newBuilder().setRoot(toEntryData(documentTree.getRoot())).build());
|
||||
return new DocumentStructure(toEntryData(documentTree.getRoot()));
|
||||
}
|
||||
|
||||
|
||||
private EntryData toEntryData(DocumentTree.Entry entry) {
|
||||
private DocumentStructure.EntryData toEntryData(DocumentTree.Entry entry) {
|
||||
|
||||
List<Long> atomicTextBlocks;
|
||||
Long[] atomicTextBlocks;
|
||||
|
||||
if (entry.getNode().isLeaf()) {
|
||||
atomicTextBlocks = toAtomicTextBlockIds(entry.getNode().getLeafTextBlock());
|
||||
} else {
|
||||
atomicTextBlocks = new ArrayList<>();
|
||||
atomicTextBlocks = new Long[]{};
|
||||
}
|
||||
|
||||
Map<String, String> properties = switch (entry.getType()) {
|
||||
@@ -102,81 +81,77 @@ public class DocumentDataMapper {
|
||||
default -> new HashMap<>();
|
||||
};
|
||||
|
||||
var documentBuilder = EntryData.newBuilder()
|
||||
.addAllTreeId(entry.getTreeId())
|
||||
.addAllChildren(entry.getChildren()
|
||||
.stream()
|
||||
.map(DocumentDataMapper::toEntryData)
|
||||
.toList())
|
||||
.setType(entry.getType())
|
||||
.addAllAtomicBlockIds(atomicTextBlocks)
|
||||
.addAllPageNumbers(entry.getNode().getPages()
|
||||
.stream()
|
||||
.map(Page::getNumber)
|
||||
.map(Integer::longValue)
|
||||
.toList())
|
||||
.putAllProperties(properties);
|
||||
DocumentStructure.EntryData.EntryDataBuilder documentBuilder = DocumentStructure.EntryData.builder()
|
||||
.treeId(toPrimitiveIntArray(entry.getTreeId()))
|
||||
.children(entry.getChildren().stream().map(DocumentDataMapper::toEntryData).toList())
|
||||
.type(entry.getType())
|
||||
.atomicBlockIds(atomicTextBlocks)
|
||||
.pageNumbers(entry.getNode().getPages().stream().map(Page::getNumber).map(Integer::longValue).toArray(Long[]::new))
|
||||
.properties(properties);
|
||||
if (entry.getNode() != null) {
|
||||
documentBuilder.addAllEngines(entry.getNode().getEngines());
|
||||
documentBuilder.engines(entry.getNode().getEngines());
|
||||
} else {
|
||||
documentBuilder.addAllEngines(new HashSet<>(Set.of(LayoutEngine.ALGORITHM)));
|
||||
documentBuilder.engines(new HashSet<>(Set.of(LayoutEngine.ALGORITHM)));
|
||||
}
|
||||
return documentBuilder.build();
|
||||
}
|
||||
|
||||
|
||||
private List<Long> toAtomicTextBlockIds(TextBlock textBlock) {
|
||||
private Long[] toAtomicTextBlockIds(TextBlock textBlock) {
|
||||
|
||||
return textBlock.getAtomicTextBlocks()
|
||||
.stream()
|
||||
.map(AtomicTextBlock::getId)
|
||||
.toList();
|
||||
return textBlock.getAtomicTextBlocks().stream().map(AtomicTextBlock::getId).toArray(Long[]::new);
|
||||
}
|
||||
|
||||
|
||||
private DocumentPage toPageData(Page p) {
|
||||
|
||||
return DocumentPage.newBuilder().setRotation(p.getRotation()).setHeight(p.getHeight()).setWidth(p.getWidth()).setNumber(p.getNumber()).build();
|
||||
return DocumentPage.builder().rotation(p.getRotation()).height(p.getHeight()).width(p.getWidth()).number(p.getNumber()).build();
|
||||
}
|
||||
|
||||
|
||||
private DocumentTextData toAtomicTextBlockData(AtomicTextBlock atomicTextBlock) {
|
||||
|
||||
return DocumentTextData.newBuilder()
|
||||
.setId(atomicTextBlock.getId())
|
||||
.setPage(atomicTextBlock.getPage().getNumber().longValue())
|
||||
.setSearchText(atomicTextBlock.getSearchText())
|
||||
.setNumberOnPage(atomicTextBlock.getNumberOnPage())
|
||||
.setStart(atomicTextBlock.getTextRange().start())
|
||||
.setEnd(atomicTextBlock.getTextRange().end())
|
||||
.addAllLineBreaks(atomicTextBlock.getLineBreaks())
|
||||
return DocumentTextData.builder()
|
||||
.id(atomicTextBlock.getId())
|
||||
.page(atomicTextBlock.getPage().getNumber().longValue())
|
||||
.searchText(atomicTextBlock.getSearchText())
|
||||
.numberOnPage(atomicTextBlock.getNumberOnPage())
|
||||
.start(atomicTextBlock.getTextRange().start())
|
||||
.end(atomicTextBlock.getTextRange().end())
|
||||
.lineBreaks(toPrimitiveIntArray(atomicTextBlock.getLineBreaks()))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
private DocumentPositionData toAtomicPositionBlockData(AtomicTextBlock atomicTextBlock) {
|
||||
|
||||
return DocumentPositionData.newBuilder()
|
||||
.setId(atomicTextBlock.getId())
|
||||
.addAllPositions(toPositions(atomicTextBlock.getPositions()))
|
||||
.addAllStringIdxToPositionIdx(atomicTextBlock.getStringIdxToPositionIdx())
|
||||
return DocumentPositionData.builder()
|
||||
.id(atomicTextBlock.getId())
|
||||
.positions(toPrimitiveFloatMatrix(atomicTextBlock.getPositions()))
|
||||
.stringIdxToPositionIdx(toPrimitiveIntArray(atomicTextBlock.getStringIdxToPositionIdx()))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
private static List<Position> toPositions(List<Rectangle2D> rects) {
|
||||
private static float[][] toPrimitiveFloatMatrix(List<Rectangle2D> positions) {
|
||||
|
||||
List<Position> positions = new ArrayList<>();
|
||||
for (Rectangle2D rect : rects) {
|
||||
positions.add(toPosition(rect));
|
||||
float[][] positionMatrix = new float[positions.size()][];
|
||||
for (int i = 0; i < positions.size(); i++) {
|
||||
positionMatrix[i] = toArray(positions.get(i));
|
||||
}
|
||||
return positions;
|
||||
return positionMatrix;
|
||||
}
|
||||
|
||||
|
||||
private static Position toPosition(Rectangle2D rect) {
|
||||
private static float[] toArray(Rectangle2D positions) {
|
||||
|
||||
return Position.newBuilder().addValue((float) rect.getMinX()).addValue((float) rect.getMinY()).addValue((float) rect.getWidth()).addValue((float) rect.getHeight()).build();
|
||||
return new float[]{(float) positions.getMinX(), (float) positions.getMinY(), (float) positions.getWidth(), (float) positions.getHeight()};
|
||||
}
|
||||
|
||||
|
||||
private int[] toPrimitiveIntArray(List<Integer> list) {
|
||||
|
||||
return list.stream().mapToInt(Integer::intValue).toArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+31
-33
@@ -8,10 +8,10 @@ import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPageProto.DocumentPage;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPositionDataProto.AllDocumentPositionData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentTextDataProto.AllDocumentTextData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.EntryDataProto.EntryData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPage;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPositionData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructure;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentTextData;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.DocumentTree;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.DuplicatedParagraph;
|
||||
@@ -41,29 +41,27 @@ public class DocumentGraphMapper {
|
||||
DocumentTree documentTree = new DocumentTree(document);
|
||||
Context context = new Context(documentData, documentTree);
|
||||
|
||||
context.pages.addAll(documentData.getDocumentPages().getDocumentPagesList()
|
||||
.stream()
|
||||
context.pages.addAll(Arrays.stream(documentData.getDocumentPages())
|
||||
.map(DocumentGraphMapper::buildPage)
|
||||
.toList());
|
||||
|
||||
context.documentTree.getRoot().getChildren().addAll(buildEntries(documentData.getDocumentStructure().getRoot().getChildrenList(), context));
|
||||
context.documentTree.getRoot().getChildren().addAll(buildEntries(documentData.getDocumentStructure().getRoot().getChildren(), context));
|
||||
|
||||
document.setDocumentTree(context.documentTree);
|
||||
document.setPages(new HashSet<>(context.pages));
|
||||
document.setNumberOfPages(documentData.getDocumentPages().getDocumentPagesCount());
|
||||
document.setNumberOfPages(documentData.getDocumentPages().length);
|
||||
|
||||
document.setTextBlock(document.getTextBlock());
|
||||
return document;
|
||||
}
|
||||
|
||||
|
||||
private List<DocumentTree.Entry> buildEntries(List<EntryData> entries, Context context) {
|
||||
private List<DocumentTree.Entry> buildEntries(List<DocumentStructure.EntryData> entries, Context context) {
|
||||
|
||||
List<DocumentTree.Entry> newEntries = new LinkedList<>();
|
||||
for (EntryData entryData : entries) {
|
||||
for (DocumentStructure.EntryData entryData : entries) {
|
||||
|
||||
List<Page> pages = entryData.getPageNumbersList()
|
||||
.stream()
|
||||
List<Page> pages = Arrays.stream(entryData.getPageNumbers())
|
||||
.map(pageNumber -> getPage(pageNumber, context))
|
||||
.toList();
|
||||
|
||||
@@ -76,12 +74,12 @@ public class DocumentGraphMapper {
|
||||
case FOOTER -> buildFooter(context);
|
||||
case TABLE -> buildTable(context, entryData.getProperties());
|
||||
case TABLE_CELL -> buildTableCell(context, entryData.getProperties());
|
||||
case IMAGE -> buildImage(context, entryData.getProperties(), entryData.getPageNumbersList());
|
||||
case IMAGE -> buildImage(context, entryData.getProperties(), entryData.getPageNumbers());
|
||||
default -> throw new UnsupportedOperationException("Not yet implemented for type " + entryData.getType());
|
||||
};
|
||||
|
||||
if (entryData.getAtomicBlockIdsCount() > 0) {
|
||||
TextBlock textBlock = toTextBlock(entryData.getAtomicBlockIdsList(), context, node);
|
||||
if (entryData.getAtomicBlockIds().length > 0) {
|
||||
TextBlock textBlock = toTextBlock(entryData.getAtomicBlockIds(), context, node);
|
||||
node.setLeafTextBlock(textBlock);
|
||||
switch (entryData.getType()) {
|
||||
case HEADER -> pages.forEach(page -> page.setHeader((Header) node));
|
||||
@@ -91,10 +89,11 @@ public class DocumentGraphMapper {
|
||||
.forEach(atb -> atb.getPage().getTextBlocksOnPage().add(atb));
|
||||
}
|
||||
}
|
||||
List<Integer> treeId = entryData.getTreeIdList();
|
||||
List<Integer> treeId = Arrays.stream(entryData.getTreeId()).boxed()
|
||||
.toList();
|
||||
node.setTreeId(treeId);
|
||||
|
||||
newEntries.add(DocumentTree.Entry.builder().treeId(treeId).children(buildEntries(entryData.getChildrenList(), context)).node(node).build());
|
||||
newEntries.add(DocumentTree.Entry.builder().treeId(treeId).children(buildEntries(entryData.getChildren(), context)).node(node).build());
|
||||
}
|
||||
return newEntries;
|
||||
}
|
||||
@@ -106,10 +105,10 @@ public class DocumentGraphMapper {
|
||||
}
|
||||
|
||||
|
||||
private Image buildImage(Context context, Map<String, String> properties, List<Long> pageNumbers) {
|
||||
private Image buildImage(Context context, Map<String, String> properties, Long[] pageNumbers) {
|
||||
|
||||
assert pageNumbers.size() == 1;
|
||||
Page page = getPage(pageNumbers.get(0), context);
|
||||
assert pageNumbers.length == 1;
|
||||
Page page = getPage(pageNumbers[0], context);
|
||||
var builder = Image.builder();
|
||||
PropertiesMapper.parseImageProperties(properties, builder);
|
||||
return builder.documentTree(context.documentTree).page(page).build();
|
||||
@@ -162,7 +161,7 @@ public class DocumentGraphMapper {
|
||||
|
||||
DuplicatedParagraph duplicatedParagraph = DuplicatedParagraph.builder().documentTree(context.documentTree).build();
|
||||
|
||||
var unsortedTextblockIds = PropertiesMapper.getUnsortedTextblockIds(properties);
|
||||
Long[] unsortedTextblockIds = PropertiesMapper.getUnsortedTextblockIds(properties);
|
||||
duplicatedParagraph.setUnsortedLeafTextBlock(toTextBlock(unsortedTextblockIds, context, duplicatedParagraph));
|
||||
return duplicatedParagraph;
|
||||
|
||||
@@ -172,9 +171,9 @@ public class DocumentGraphMapper {
|
||||
}
|
||||
|
||||
|
||||
private TextBlock toTextBlock(List<Long> atomicTextBlockIds, Context context, SemanticNode parent) {
|
||||
private TextBlock toTextBlock(Long[] atomicTextBlockIds, Context context, SemanticNode parent) {
|
||||
|
||||
return atomicTextBlockIds.stream()
|
||||
return Arrays.stream(atomicTextBlockIds)
|
||||
.map(atomicTextBlockId -> getAtomicTextBlock(context, parent, atomicTextBlockId))
|
||||
.collect(new TextBlockCollector());
|
||||
}
|
||||
@@ -182,13 +181,10 @@ public class DocumentGraphMapper {
|
||||
|
||||
private AtomicTextBlock getAtomicTextBlock(Context context, SemanticNode parent, Long atomicTextBlockId) {
|
||||
|
||||
return AtomicTextBlock.fromAtomicTextBlockData(context.documentTextDataBlockData.getDocumentTextDataList()
|
||||
.get(Math.toIntExact(atomicTextBlockId)),
|
||||
context.atomicPositionBlockData.getDocumentPositionDataList()
|
||||
.get(Math.toIntExact(atomicTextBlockId)),
|
||||
return AtomicTextBlock.fromAtomicTextBlockData(context.documentTextDataBlockData.get(Math.toIntExact(atomicTextBlockId)),
|
||||
context.atomicPositionBlockData.get(Math.toIntExact(atomicTextBlockId)),
|
||||
parent,
|
||||
getPage(context.documentTextDataBlockData.getDocumentTextDataList()
|
||||
.get(Math.toIntExact(atomicTextBlockId)).getPage(), context));
|
||||
getPage(context.documentTextDataBlockData.get(Math.toIntExact(atomicTextBlockId)).getPage(), context));
|
||||
}
|
||||
|
||||
|
||||
@@ -211,16 +207,18 @@ public class DocumentGraphMapper {
|
||||
|
||||
private final DocumentTree documentTree;
|
||||
private final List<Page> pages;
|
||||
private final AllDocumentTextData documentTextDataBlockData;
|
||||
private final AllDocumentPositionData atomicPositionBlockData;
|
||||
private final List<DocumentTextData> documentTextDataBlockData;
|
||||
private final List<DocumentPositionData> atomicPositionBlockData;
|
||||
|
||||
|
||||
Context(DocumentData documentData, DocumentTree documentTree) {
|
||||
|
||||
this.documentTree = documentTree;
|
||||
this.pages = new LinkedList<>();
|
||||
this.documentTextDataBlockData = documentData.getDocumentTextData();
|
||||
this.atomicPositionBlockData = documentData.getDocumentPositions();
|
||||
this.documentTextDataBlockData = Arrays.stream(documentData.getDocumentTextData())
|
||||
.toList();
|
||||
this.atomicPositionBlockData = Arrays.stream(documentData.getDocumentPositions())
|
||||
.toList();
|
||||
|
||||
}
|
||||
|
||||
|
||||
+37
-48
@@ -3,11 +3,10 @@ package com.knecon.fforesight.service.layoutparser.processor.services.mapper;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructureWrapper;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructure;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.DuplicatedParagraph;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Image;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.ImageType;
|
||||
@@ -21,11 +20,11 @@ public class PropertiesMapper {
|
||||
public static Map<String, String> buildImageProperties(Image image) {
|
||||
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
properties.put(DocumentStructureWrapper.ImageProperties.IMAGE_TYPE, image.getImageType().toString());
|
||||
properties.put(DocumentStructureWrapper.ImageProperties.TRANSPARENT, String.valueOf(image.isTransparent()));
|
||||
properties.put(DocumentStructureWrapper.ImageProperties.POSITION, toString(image.getPosition()));
|
||||
properties.put(DocumentStructureWrapper.ImageProperties.ID, image.getId());
|
||||
properties.put(DocumentStructureWrapper.ImageProperties.REPRESENTATION_HASH, image.getRepresentationHash());
|
||||
properties.put(DocumentStructure.ImageProperties.IMAGE_TYPE, image.getImageType().toString());
|
||||
properties.put(DocumentStructure.ImageProperties.TRANSPARENT, String.valueOf(image.isTransparent()));
|
||||
properties.put(DocumentStructure.ImageProperties.POSITION, toString(image.getPosition()));
|
||||
properties.put(DocumentStructure.ImageProperties.ID, image.getId());
|
||||
properties.put(DocumentStructure.ImageProperties.REPRESENTATION_HASH, image.getRepresentationHash());
|
||||
return properties;
|
||||
}
|
||||
|
||||
@@ -33,19 +32,15 @@ public class PropertiesMapper {
|
||||
public static Map<String, String> buildTableCellProperties(TableCell tableCell) {
|
||||
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
properties.put(DocumentStructureWrapper.TableCellProperties.ROW, String.valueOf(tableCell.getRow()));
|
||||
properties.put(DocumentStructureWrapper.TableCellProperties.COL, String.valueOf(tableCell.getCol()));
|
||||
properties.put(DocumentStructureWrapper.TableCellProperties.HEADER, String.valueOf(tableCell.isHeader()));
|
||||
properties.put(DocumentStructure.TableCellProperties.ROW, String.valueOf(tableCell.getRow()));
|
||||
properties.put(DocumentStructure.TableCellProperties.COL, String.valueOf(tableCell.getCol()));
|
||||
properties.put(DocumentStructure.TableCellProperties.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 = toString(tableCell.getBBox()
|
||||
.get(tableCell.getPages()
|
||||
.stream()
|
||||
.findFirst()
|
||||
.get()));
|
||||
properties.put(DocumentStructureWrapper.TableCellProperties.B_BOX, bBoxString);
|
||||
String bBoxString = toString(tableCell.getBBox().get(tableCell.getPages().stream().findFirst().get()));
|
||||
properties.put(DocumentStructure.TableCellProperties.B_BOX, bBoxString);
|
||||
|
||||
return properties;
|
||||
}
|
||||
@@ -54,62 +49,59 @@ public class PropertiesMapper {
|
||||
public static Map<String, String> buildTableProperties(Table table) {
|
||||
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
properties.put(DocumentStructureWrapper.TableProperties.NUMBER_OF_ROWS, String.valueOf(table.getNumberOfRows()));
|
||||
properties.put(DocumentStructureWrapper.TableProperties.NUMBER_OF_COLS, String.valueOf(table.getNumberOfCols()));
|
||||
properties.put(DocumentStructure.TableProperties.NUMBER_OF_ROWS, String.valueOf(table.getNumberOfRows()));
|
||||
properties.put(DocumentStructure.TableProperties.NUMBER_OF_COLS, String.valueOf(table.getNumberOfCols()));
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
||||
public static void parseImageProperties(Map<String, String> properties, Image.ImageBuilder builder) {
|
||||
|
||||
builder.imageType(parseImageType(properties.get(DocumentStructureWrapper.ImageProperties.IMAGE_TYPE)));
|
||||
builder.transparent(Boolean.parseBoolean(properties.get(DocumentStructureWrapper.ImageProperties.TRANSPARENT)));
|
||||
builder.position(DocumentStructureWrapper.parseRectangle2D(properties.get(DocumentStructureWrapper.ImageProperties.POSITION)));
|
||||
builder.imageType(parseImageType(properties.get(DocumentStructure.ImageProperties.IMAGE_TYPE)));
|
||||
builder.transparent(Boolean.parseBoolean(properties.get(DocumentStructure.ImageProperties.TRANSPARENT)));
|
||||
builder.position(DocumentStructure.parseRectangle2D(properties.get(DocumentStructure.ImageProperties.POSITION)));
|
||||
}
|
||||
|
||||
|
||||
public static void parseTableCellProperties(Map<String, String> properties, TableCell.TableCellBuilder builder) {
|
||||
|
||||
builder.row(Integer.parseInt(properties.get(DocumentStructureWrapper.TableCellProperties.ROW)));
|
||||
builder.col(Integer.parseInt(properties.get(DocumentStructureWrapper.TableCellProperties.COL)));
|
||||
builder.header(Boolean.parseBoolean(properties.get(DocumentStructureWrapper.TableCellProperties.HEADER)));
|
||||
builder.bBox(DocumentStructureWrapper.parseRectangle2D(properties.get(DocumentStructureWrapper.TableCellProperties.B_BOX)));
|
||||
builder.row(Integer.parseInt(properties.get(DocumentStructure.TableCellProperties.ROW)));
|
||||
builder.col(Integer.parseInt(properties.get(DocumentStructure.TableCellProperties.COL)));
|
||||
builder.header(Boolean.parseBoolean(properties.get(DocumentStructure.TableCellProperties.HEADER)));
|
||||
builder.bBox(DocumentStructure.parseRectangle2D(properties.get(DocumentStructure.TableCellProperties.B_BOX)));
|
||||
}
|
||||
|
||||
|
||||
public static void parseTableProperties(Map<String, String> properties, Table.TableBuilder builder) {
|
||||
|
||||
builder.numberOfRows(Integer.parseInt(properties.get(DocumentStructureWrapper.TableProperties.NUMBER_OF_ROWS)));
|
||||
builder.numberOfCols(Integer.parseInt(properties.get(DocumentStructureWrapper.TableProperties.NUMBER_OF_COLS)));
|
||||
builder.numberOfRows(Integer.parseInt(properties.get(DocumentStructure.TableProperties.NUMBER_OF_ROWS)));
|
||||
builder.numberOfCols(Integer.parseInt(properties.get(DocumentStructure.TableProperties.NUMBER_OF_COLS)));
|
||||
}
|
||||
|
||||
|
||||
public static Map<String, String> buildDuplicateParagraphProperties(DuplicatedParagraph duplicatedParagraph) {
|
||||
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
properties.put(DocumentStructureWrapper.DuplicateParagraphProperties.UNSORTED_TEXTBLOCK_ID,
|
||||
Arrays.toString(toAtomicTextBlockIds(duplicatedParagraph.getUnsortedLeafTextBlock())));
|
||||
properties.put(DocumentStructure.DuplicateParagraphProperties.UNSORTED_TEXTBLOCK_ID, Arrays.toString(toAtomicTextBlockIds(duplicatedParagraph.getUnsortedLeafTextBlock())));
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isDuplicateParagraph(Map<String, String> properties) {
|
||||
|
||||
return properties.containsKey(DocumentStructureWrapper.DuplicateParagraphProperties.UNSORTED_TEXTBLOCK_ID);
|
||||
return properties.containsKey(DocumentStructure.DuplicateParagraphProperties.UNSORTED_TEXTBLOCK_ID);
|
||||
}
|
||||
|
||||
|
||||
public static List<Long> getUnsortedTextblockIds(Map<String, String> properties) {
|
||||
public static Long[] getUnsortedTextblockIds(Map<String, String> properties) {
|
||||
|
||||
return toLongList(properties.get(DocumentStructureWrapper.DuplicateParagraphProperties.UNSORTED_TEXTBLOCK_ID));
|
||||
return toLongArray(properties.get(DocumentStructure.DuplicateParagraphProperties.UNSORTED_TEXTBLOCK_ID));
|
||||
}
|
||||
|
||||
|
||||
public static List<Long> toLongList(String ids) {
|
||||
public static Long[] toLongArray(String ids) {
|
||||
|
||||
return Arrays.stream(ids.substring(1, ids.length() - 1).trim().split(","))
|
||||
.map(Long::valueOf)
|
||||
.toList();
|
||||
return Arrays.stream(ids.substring(1, ids.length() - 1).trim().split(",")).map(Long::valueOf).toArray(Long[]::new);
|
||||
}
|
||||
|
||||
|
||||
@@ -128,23 +120,20 @@ public class PropertiesMapper {
|
||||
public static String toString(Rectangle2D rectangle2D) {
|
||||
|
||||
return String.format(Locale.US,
|
||||
"%f%s%f%s%f%s%f",
|
||||
rectangle2D.getX(),
|
||||
DocumentStructureWrapper.RECTANGLE_DELIMITER,
|
||||
rectangle2D.getY(),
|
||||
DocumentStructureWrapper.RECTANGLE_DELIMITER,
|
||||
rectangle2D.getWidth(),
|
||||
DocumentStructureWrapper.RECTANGLE_DELIMITER,
|
||||
rectangle2D.getHeight());
|
||||
"%f%s%f%s%f%s%f",
|
||||
rectangle2D.getX(),
|
||||
DocumentStructure.RECTANGLE_DELIMITER,
|
||||
rectangle2D.getY(),
|
||||
DocumentStructure.RECTANGLE_DELIMITER,
|
||||
rectangle2D.getWidth(),
|
||||
DocumentStructure.RECTANGLE_DELIMITER,
|
||||
rectangle2D.getHeight());
|
||||
}
|
||||
|
||||
|
||||
private static Long[] toAtomicTextBlockIds(TextBlock textBlock) {
|
||||
|
||||
return textBlock.getAtomicTextBlocks()
|
||||
.stream()
|
||||
.map(AtomicTextBlock::getId)
|
||||
.toArray(Long[]::new);
|
||||
return textBlock.getAtomicTextBlocks().stream().map(AtomicTextBlock::getId).toArray(Long[]::new);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.taas.ParagraphData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.taas.Range;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.taas.ResearchDocumentData;
|
||||
|
||||
+15
-15
@@ -40,7 +40,7 @@ import org.apache.pdfbox.text.TextPosition;
|
||||
|
||||
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 com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -52,7 +52,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
|
||||
private final static Set<String> DOT_LIKE_CHARACTERS = Set.of(".", "·", "•", "․", "‧", "∙", "⋅", "・", ".", "・", "…", "⸱", "﹒", "ꞏ");
|
||||
private final List<TextPositionSequence> textPositionSequences = new ArrayList<>();
|
||||
private final List<Word> words = new ArrayList<>();
|
||||
private final List<Ruling> rulings = new ArrayList<>();
|
||||
private final List<Ruling> graphicsPath = new ArrayList<>();
|
||||
@Setter
|
||||
@@ -246,10 +246,10 @@ public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
direction = textPositions.get(i).getDir();
|
||||
}
|
||||
|
||||
if (!textPositionSequences.isEmpty()) {
|
||||
previous = textPositionSequences.get(textPositionSequences.size() - 1)
|
||||
if (!words.isEmpty()) {
|
||||
previous = words.get(words.size() - 1)
|
||||
.getTextPositions()
|
||||
.get(textPositionSequences.get(textPositionSequences.size() - 1).getTextPositions().size() - 1);
|
||||
.get(words.get(words.size() - 1).getTextPositions().size() - 1);
|
||||
}
|
||||
|
||||
if (i == 0 && (textPositions.get(i).getUnicode().equals(" ") || textPositions.get(i).getUnicode().equals("\u00A0") || textPositions.get(i).getUnicode().equals("\t"))) {
|
||||
@@ -259,7 +259,7 @@ public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
|
||||
if (textPositions.get(i).getDir() != direction && startIndex != i) {
|
||||
List<TextPosition> sublist = textPositions.subList(startIndex, i);
|
||||
textPositionSequences.add(new TextPositionSequence(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart));
|
||||
words.add(new Word(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart));
|
||||
startIndex = i;
|
||||
direction = textPositions.get(i).getDir();
|
||||
}
|
||||
@@ -268,7 +268,7 @@ public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
if (checkIfCurrentPositionIsToTheRightOfPreviousPosition(i, textPositions)) {
|
||||
List<TextPosition> sublist = textPositions.subList(startIndex, i);
|
||||
if (checkIfSequenceContainsOnlyWhitespaces(sublist)) {
|
||||
textPositionSequences.add(new TextPositionSequence(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart));
|
||||
words.add(new Word(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart));
|
||||
}
|
||||
startIndex = i;
|
||||
}
|
||||
@@ -276,7 +276,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<TextPosition> sublist = textPositions.subList(startIndex, i);
|
||||
if (checkIfSequenceContainsOnlyWhitespaces(sublist)) {
|
||||
textPositionSequences.add(new TextPositionSequence(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart));
|
||||
words.add(new Word(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart));
|
||||
}
|
||||
startIndex = i;
|
||||
}
|
||||
@@ -290,22 +290,22 @@ public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
// Remove false sequence ends (whitespaces)
|
||||
if (checkIfGapSizeBetweenCharactersSmallerThanMaximum(previous, sublist, 0.01f)) {
|
||||
for (TextPosition t : sublist) {
|
||||
textPositionSequences.get(textPositionSequences.size() - 1).add(t);
|
||||
words.get(words.size() - 1).add(t);
|
||||
}
|
||||
} else {
|
||||
textPositionSequences.add(new TextPositionSequence(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart));
|
||||
words.add(new Word(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart));
|
||||
}
|
||||
}
|
||||
startIndex = i + 1;
|
||||
}
|
||||
if (isDottedLineFollowedByWord(textPositions, i, startIndex)) {
|
||||
List<TextPosition> sublist = textPositions.subList(startIndex, i);
|
||||
textPositionSequences.add(new TextPositionSequence(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart));
|
||||
words.add(new Word(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart));
|
||||
startIndex = i;
|
||||
}
|
||||
if (isWordFollowedByDottedLine(textPositions, i, startIndex)) {
|
||||
List<TextPosition> sublist = textPositions.subList(startIndex, i - 2);
|
||||
textPositionSequences.add(new TextPositionSequence(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart));
|
||||
words.add(new Word(sublist, pageNumber, i == textPositions.size() - 1 && isParagraphStart));
|
||||
startIndex = i - 2;
|
||||
}
|
||||
}
|
||||
@@ -324,10 +324,10 @@ public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
&& sublist.get(0).getYDirAdj() == previous.getYDirAdj()
|
||||
&& sublist.get(0).getXDirAdj() - (previous.getXDirAdj() + previous.getWidthDirAdj()) < 0.01) {
|
||||
for (TextPosition t : sublist) {
|
||||
textPositionSequences.get(textPositionSequences.size() - 1).add(t);
|
||||
words.get(words.size() - 1).add(t);
|
||||
}
|
||||
} else {
|
||||
textPositionSequences.add(new TextPositionSequence(sublist, pageNumber, isParagraphStart));
|
||||
words.add(new Word(sublist, pageNumber, isParagraphStart));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,7 +392,7 @@ public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
@Override
|
||||
public String getText(PDDocument doc) throws IOException {
|
||||
|
||||
textPositionSequences.clear();
|
||||
words.clear();
|
||||
rulings.clear();
|
||||
graphicsPath.clear();
|
||||
path_x = 0.0f;
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ import org.apache.pdfbox.text.TextPosition;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.processor.docstrum.model.BoundingBox;
|
||||
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.model.text.Word;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@@ -48,7 +48,7 @@ public class MarkedContentUtils {
|
||||
|
||||
return markedContentByYPosition.values()
|
||||
.stream()
|
||||
.map(textPositions -> new TextPositionSequence(textPositions, 0, true).getBBoxPdf())
|
||||
.map(textPositions -> new Word(textPositions, 0, true).getBBoxPdf())
|
||||
.map(t -> new Rectangle2D.Double(t.getX(), t.getY() - Math.abs(t.getHeight()), t.getWidth(), Math.abs(t.getHeight())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public class MarkedContentUtils {
|
||||
.filter(content -> content instanceof TextPosition)
|
||||
.map(content -> (TextPosition) content)
|
||||
.filter(content -> !content.getUnicode().equals(" "))
|
||||
.map(textPositions -> new TextPositionSequence(List.of(textPositions), 0, true))
|
||||
.map(textPositions -> new Word(List.of(textPositions), 0, true))
|
||||
.map(BoundingBox::getBBoxPdf)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
+1
-2
@@ -14,7 +14,7 @@ import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.DocumentTree;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Page;
|
||||
@@ -117,7 +117,6 @@ public class PdfVisualisationUtility {
|
||||
case TABLE -> Color.ORANGE;
|
||||
case TABLE_CELL -> Color.GRAY;
|
||||
case IMAGE -> Color.MAGENTA;
|
||||
case UNRECOGNIZED -> Color.PINK;
|
||||
}).build();
|
||||
}
|
||||
|
||||
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
package com.knecon.fforesight.service.layoutparser.processor.utils;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import com.google.protobuf.MessageOrBuilder;
|
||||
import com.google.protobuf.Struct;
|
||||
import com.google.protobuf.util.JsonFormat;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class ProtobufUtil {
|
||||
|
||||
public static String toJson(MessageOrBuilder messageOrBuilder) throws IOException {
|
||||
return JsonFormat.printer().print(messageOrBuilder);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Message fromJson(String json) throws IOException {
|
||||
Message.Builder structBuilder = Struct.newBuilder();
|
||||
JsonFormat.parser().ignoringUnknownFields().merge(json, structBuilder);
|
||||
return structBuilder.build();
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public <T extends Message> File serializeToTempFile(T any) {
|
||||
var tempFile = File.createTempFile("storage-protobuf", ".data");
|
||||
|
||||
try (var fos = new GZIPOutputStream(new BufferedOutputStream(new FileOutputStream(tempFile)))) {
|
||||
any.writeTo(fos);
|
||||
return tempFile;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+22
-22
@@ -16,7 +16,7 @@ import com.knecon.fforesight.service.layoutparser.processor.docstrum.model.Union
|
||||
import com.knecon.fforesight.service.layoutparser.processor.docstrum.utils.DoubleUtils;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.docstrum.utils.FastAtan2;
|
||||
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.model.text.Word;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@@ -36,33 +36,33 @@ public class TextPositionOperations {
|
||||
.thenComparing(TextBoundingBox::getXDirAdj, (o1, o2) -> DoubleUtils.compareDouble(o1, o2, X_THRESHOLD));
|
||||
|
||||
|
||||
public List<TextPositionSequence> mergeAndSort(List<TextPageBlock> textBlocks) {
|
||||
public List<Word> mergeAndSort(List<TextPageBlock> textBlocks) {
|
||||
|
||||
var sequences = textBlocks.stream()
|
||||
.flatMap(tb -> tb.getSequences()
|
||||
.flatMap(tb -> tb.getWords()
|
||||
.stream())
|
||||
.collect(Collectors.toSet());
|
||||
return sortUsingLineDetection(sequences);
|
||||
}
|
||||
|
||||
|
||||
public List<TextPositionSequence> sort(List<TextPositionSequence> sequences) {
|
||||
public List<Word> sort(List<Word> sequences) {
|
||||
|
||||
return sortUsingLineDetection(new HashSet<>(sequences));
|
||||
}
|
||||
|
||||
|
||||
private List<TextPositionSequence> sortUsingLineDetection(Set<TextPositionSequence> sequences) {
|
||||
private List<Word> sortUsingLineDetection(Set<Word> sequences) {
|
||||
|
||||
return sortLines(groupByLine(sequences));
|
||||
}
|
||||
|
||||
|
||||
public List<TextPositionSequence> sortLines(Collection<Set<TextPositionSequence>> lines) {
|
||||
public List<Word> sortLines(Collection<Set<Word>> lines) {
|
||||
|
||||
List<List<TextPositionSequence>> lineBlocks = new ArrayList<>();
|
||||
for (Set<TextPositionSequence> line : lines) {
|
||||
List<TextPositionSequence> sortedLine = sortByXDirAdj(line);
|
||||
List<List<Word>> lineBlocks = new ArrayList<>();
|
||||
for (Set<Word> line : lines) {
|
||||
List<Word> sortedLine = sortByXDirAdj(line);
|
||||
if (!sortedLine.isEmpty()) {
|
||||
lineBlocks.add(sortedLine);
|
||||
}
|
||||
@@ -70,35 +70,35 @@ public class TextPositionOperations {
|
||||
// need to use old sorting, since COMPARATOR_DIR_ADJ is not transitive
|
||||
QuickSort.sort(lineBlocks, Comparator.comparing(line -> line.get(0), COMPARATOR_DIR_ADJ));
|
||||
|
||||
List<TextPositionSequence> list = new ArrayList<>();
|
||||
for (List<TextPositionSequence> textPositionSequences : lineBlocks) {
|
||||
list.addAll(textPositionSequences);
|
||||
List<Word> list = new ArrayList<>();
|
||||
for (List<Word> words : lineBlocks) {
|
||||
list.addAll(words);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
private List<TextPositionSequence> sortByXDirAdj(Set<TextPositionSequence> line) {
|
||||
private List<Word> sortByXDirAdj(Set<Word> line) {
|
||||
|
||||
return line.stream()
|
||||
.sorted(Comparator.comparing(TextPositionSequence::getXDirAdj))
|
||||
.sorted(Comparator.comparing(Word::getXDirAdj))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
public Collection<Set<TextPositionSequence>> groupByLine(Set<TextPositionSequence> sequences) {
|
||||
public Collection<Set<Word>> groupByLine(Set<Word> sequences) {
|
||||
|
||||
double maxLineDistance = sequences.stream()
|
||||
.map(TextPositionSequence::getBBoxDirAdj)
|
||||
.map(Word::getBBoxDirAdj)
|
||||
.mapToDouble(RectangularShape::getHeight).average().orElse(10) * MAX_LINE_HEIGHT_FACTOR;
|
||||
double maxXGap = sequences.stream()
|
||||
.map(TextPositionSequence::getBBoxDirAdj)
|
||||
.map(Word::getBBoxDirAdj)
|
||||
.mapToDouble(RectangularShape::getWidth).average().orElse(75) * MAX_WORD_DISTANCE_FACTOR;
|
||||
|
||||
UnionFind<TextPositionSequence> unionFind = new UnionFind<>(sequences);
|
||||
UnionFind<Word> unionFind = new UnionFind<>(sequences);
|
||||
|
||||
for (TextPositionSequence sequence : sequences) {
|
||||
for (TextPositionSequence sequence2 : sequences) {
|
||||
for (Word sequence : sequences) {
|
||||
for (Word sequence2 : sequences) {
|
||||
|
||||
if (sequence.equals(sequence2)) { // || unionFind.inSameSet(sequence, sequence2)) doing this is actually slower than not doing it
|
||||
continue;
|
||||
@@ -144,10 +144,10 @@ public class TextPositionOperations {
|
||||
}
|
||||
|
||||
|
||||
public List<TextPositionSequence> merge(List<TextPageBlock> textBlocks) {
|
||||
public List<Word> merge(List<TextPageBlock> textBlocks) {
|
||||
|
||||
return textBlocks.stream()
|
||||
.map(TextPageBlock::getSequences)
|
||||
.map(TextPageBlock::getWords)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
+26
-10
@@ -27,7 +27,9 @@ import com.knecon.fforesight.service.layoutparser.processor.model.outline.Outlin
|
||||
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.model.text.ListIdentifier;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextBlockOnPage;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.text.Word;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.services.classification.NumberWord;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.utils.CoordinateTransforms;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.utils.MarkedContentUtils;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.utils.PageInformation;
|
||||
@@ -58,14 +60,14 @@ public class LayoutDebugLayer extends LayoutDebugLayerConfig {
|
||||
Map<Integer, AtomicInteger> outlineObjectsWithoutPointsPerPage = new HashMap<>();
|
||||
|
||||
|
||||
public void addTextVisualizations(List<TextPositionSequence> textPositionSequences, int pageNumber) {
|
||||
public void addTextVisualizations(List<Word> words, int pageNumber) {
|
||||
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
VisualizationsOnPage visualizationsOnPage = getOrCreateVisualizationsOnPage(pageNumber, this.words);
|
||||
visualizationsOnPage.getColoredRectangles()
|
||||
.addAll(textPositionSequences.stream()
|
||||
.addAll(words.stream()
|
||||
.map(BoundingBox::getBBoxPdf)
|
||||
.map(rect -> new ColoredRectangle(rect, WORDS_COLOR, 1))
|
||||
.toList());
|
||||
@@ -188,7 +190,7 @@ public class LayoutDebugLayer extends LayoutDebugLayerConfig {
|
||||
}
|
||||
|
||||
|
||||
public void addLineVisualizationsFromNestedTextPosition(Collection<Set<TextPositionSequence>> lines, int pageNumber) {
|
||||
public void addLineVisualizationsFromNestedTextPosition(Collection<Set<Word>> lines, int pageNumber) {
|
||||
|
||||
if (!active) {
|
||||
return;
|
||||
@@ -291,7 +293,7 @@ public class LayoutDebugLayer extends LayoutDebugLayerConfig {
|
||||
}
|
||||
|
||||
|
||||
public void addTocPages(List<TextPositionSequence> numbers, int page) {
|
||||
public void addTocPages(List<NumberWord> numbers, int page) {
|
||||
|
||||
if (!active) {
|
||||
return;
|
||||
@@ -300,13 +302,10 @@ public class LayoutDebugLayer extends LayoutDebugLayerConfig {
|
||||
VisualizationsOnPage visualizationsOnPage = getOrCreateVisualizationsOnPage(page, this.tocPages);
|
||||
visualizationsOnPage.getColoredRectangles()
|
||||
.addAll(numbers.stream()
|
||||
.map(NumberWord::word)
|
||||
.map(BoundingBox::getBBoxPdf)
|
||||
.map(line -> new ColoredRectangle(line, LINES_COLOR, LINE_WIDTH))
|
||||
.toList());
|
||||
visualizationsOnPage.getColoredRectangles()
|
||||
.add(new ColoredRectangle(numbers.stream()
|
||||
.map(BoundingBox::getBBoxPdf)
|
||||
.collect(RectangleTransformations.collectBBox()), LINES_COLOR, LINE_WIDTH));
|
||||
}
|
||||
|
||||
|
||||
@@ -330,8 +329,10 @@ public class LayoutDebugLayer extends LayoutDebugLayerConfig {
|
||||
|
||||
private void addOutlineObject(OutlineObject outlineObject, PageInformation pageInformation) {
|
||||
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
int rectSize = 5;
|
||||
|
||||
Point2D point2D;
|
||||
if (outlineObject.getPoint().isPresent()) {
|
||||
point2D = outlineObject.getPoint().get();
|
||||
@@ -355,10 +356,25 @@ public class LayoutDebugLayer extends LayoutDebugLayerConfig {
|
||||
|
||||
public void addListIdentifiers(List<ListIdentifier> listIdentifiers) {
|
||||
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
for (ListIdentifier listIdentifier : listIdentifiers) {
|
||||
getOrCreateVisualizationsOnPage(listIdentifier.getPage(), this.listIdentifiers).getColoredRectangles()
|
||||
.add(new ColoredRectangle(listIdentifier.getWord().getBBoxPdf(), WORDS_COLOR, LINE_WIDTH));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void addTocBlocks(Set<TextBlockOnPage> blocksWithNumberInCluster) {
|
||||
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
for (TextBlockOnPage textBlockOnPage : blocksWithNumberInCluster) {
|
||||
getOrCreateVisualizationsOnPage(textBlockOnPage.page().getPageNumber(), this.tocBlocks).getColoredRectangles()
|
||||
.add(new ColoredRectangle(textBlockOnPage.textBlock().getBBoxPdf(), TOC_COLOR, LINE_WIDTH));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.DuplicatedParagraph;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Headline;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Image;
|
||||
@@ -125,7 +125,7 @@ public class LayoutGrid extends LayoutGridLayerConfig {
|
||||
|
||||
Map<Page, Rectangle2D> bBoxMap = section.getBBox();
|
||||
|
||||
List<SemanticNode> subSections = section.streamAllSubNodesOfType(NodeTypeProto.NodeType.SECTION)
|
||||
List<SemanticNode> subSections = section.streamAllSubNodesOfType(NodeType.SECTION)
|
||||
.toList();
|
||||
Integer maxChildDepth = subSections.stream()
|
||||
.map(node -> node.getTreeId().size())
|
||||
|
||||
@@ -29,8 +29,8 @@ dependencies {
|
||||
implementation(project(":layoutparser-service-processor"))
|
||||
implementation(project(":layoutparser-service-internal-api"))
|
||||
|
||||
implementation("com.iqser.red.commons:storage-commons:2.50.0")
|
||||
implementation("com.knecon.fforesight:tenant-commons:0.30.0")
|
||||
implementation("com.iqser.red.commons:storage-commons:2.45.0")
|
||||
implementation("com.knecon.fforesight:tenant-commons:0.21.0")
|
||||
implementation("com.knecon.fforesight:tracing-commons:0.5.0")
|
||||
implementation("com.knecon.fforesight:lifecycle-commons:0.6.0")
|
||||
implementation("org.springframework.boot:spring-boot-starter-actuator:${springBootStarterVersion}")
|
||||
@@ -38,18 +38,15 @@ dependencies {
|
||||
implementation("com.amazonaws:aws-java-sdk-s3:1.12.536")
|
||||
implementation("org.springframework.cloud:spring-cloud-starter-openfeign:4.0.4")
|
||||
implementation("net.logstash.logback:logstash-logback-encoder:7.4")
|
||||
implementation("ch.qos.logback:logback-classic")
|
||||
|
||||
implementation("com.pdftron:PDFNet:10.11.0")
|
||||
|
||||
// for integration testing only
|
||||
testImplementation(project(":viewer-doc-processor"))
|
||||
testImplementation(project(":layoutparser-service-internal-api"))
|
||||
testImplementation("com.google.protobuf:protobuf-java-util:4.27.1")
|
||||
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-amqp:${springBootStarterVersion}")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test:${springBootStarterVersion}")
|
||||
testImplementation("com.iqser.red.service:persistence-service-shared-api-v1:2.564.0-RED9010.0")
|
||||
testImplementation("com.iqser.red.service:persistence-service-shared-api-v1:2.144.0")
|
||||
testImplementation("com.iqser.red.commons:jackson-commons:1.3.0")
|
||||
testImplementation("com.fasterxml.jackson.module:jackson-module-afterburner:${jacksonVersion}")
|
||||
testImplementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jacksonVersion}")
|
||||
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package com.knecon.fforesight.service.layoutparser.server.configuration;
|
||||
|
||||
import static com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingQueueNames.LAYOUT_PARSING_DLQ;
|
||||
|
||||
import org.springframework.amqp.core.DirectExchange;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.core.QueueBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingQueueNames;
|
||||
|
||||
@Configuration
|
||||
public class MessagingConfiguration {
|
||||
|
||||
@Bean
|
||||
public DirectExchange layoutParsingRequestExchange() {
|
||||
|
||||
return new DirectExchange(LayoutParsingQueueNames.LAYOUT_PARSING_REQUEST_EXCHANGE);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public Queue layoutParsingDLQ() {
|
||||
|
||||
return QueueBuilder.durable(LAYOUT_PARSING_DLQ).build();
|
||||
}
|
||||
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
package com.knecon.fforesight.service.layoutparser.server.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.knecon.fforesight.tenantcommons.queue.TenantMessagingConfiguration;
|
||||
|
||||
@Configuration
|
||||
public class TenantMessagingConfigurationImpl extends TenantMessagingConfiguration {
|
||||
|
||||
|
||||
}
|
||||
+3
-6
@@ -18,7 +18,6 @@ import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsi
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingRequest;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType;
|
||||
import com.knecon.fforesight.service.layoutparser.processor.LayoutParsingPipeline;
|
||||
import com.knecon.fforesight.tenantcommons.TenantContext;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
@@ -29,17 +28,15 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@RequiredArgsConstructor
|
||||
public class MessageHandler {
|
||||
|
||||
public static final String LAYOUT_PARSING_REQUEST_LISTENER_ID = "layout-parsing-request-listener";
|
||||
|
||||
private final LayoutParsingPipeline layoutParsingPipeline;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final RabbitTemplate rabbitTemplate;
|
||||
private final static String X_PIPELINE_PREFIX = "X-PIPE-";
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@RabbitHandler
|
||||
@RabbitListener(id = LAYOUT_PARSING_REQUEST_LISTENER_ID)
|
||||
@RabbitListener(queues = LayoutParsingQueueNames.LAYOUT_PARSING_REQUEST_QUEUE)
|
||||
@SneakyThrows
|
||||
public void receiveLayoutParsingRequest(Message message) {
|
||||
|
||||
LayoutParsingRequest layoutParsingRequest = objectMapper.readValue(message.getBody(), LayoutParsingRequest.class);
|
||||
@@ -60,7 +57,7 @@ public class MessageHandler {
|
||||
public void sendLayoutParsingFinishedEvent(LayoutParsingFinishedEvent layoutParsingFinishedEvent, Message message) {
|
||||
|
||||
Arrays.stream(layoutParsingFinishedEvent.message().split("\n")).forEach(log::info);
|
||||
rabbitTemplate.convertAndSend(LayoutParsingQueueNames.LAYOUT_PARSING_RESPONSE_EXCHANGE, TenantContext.getTenantId(), layoutParsingFinishedEvent, m -> {
|
||||
rabbitTemplate.convertAndSend(LayoutParsingQueueNames.LAYOUT_PARSING_FINISHED_EVENT_QUEUE, layoutParsingFinishedEvent, m -> {
|
||||
var forwardHeaders = message.getMessageProperties()
|
||||
.getHeaders()
|
||||
.entrySet()
|
||||
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package com.knecon.fforesight.service.layoutparser.server.queue;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingQueueNames;
|
||||
import com.knecon.fforesight.tenantcommons.TenantProvider;
|
||||
import com.knecon.fforesight.tenantcommons.model.TenantCreatedEvent;
|
||||
import com.knecon.fforesight.tenantcommons.model.TenantQueueConfiguration;
|
||||
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
|
||||
import com.knecon.fforesight.tenantcommons.queue.RabbitQueueFromExchangeService;
|
||||
import com.knecon.fforesight.tenantcommons.queue.TenantExchangeMessageReceiver;
|
||||
|
||||
@Service
|
||||
public class TenantExchangeMessageReceiverImpl extends TenantExchangeMessageReceiver {
|
||||
|
||||
public TenantExchangeMessageReceiverImpl(RabbitQueueFromExchangeService rabbitQueueService, TenantProvider tenantProvider) {
|
||||
|
||||
super(rabbitQueueService, tenantProvider);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Set<TenantQueueConfiguration> getTenantQueueConfigs() {
|
||||
|
||||
return Set.of(TenantQueueConfiguration.builder()
|
||||
.listenerId(MessageHandler.LAYOUT_PARSING_REQUEST_LISTENER_ID)
|
||||
.exchangeName(LayoutParsingQueueNames.LAYOUT_PARSING_REQUEST_EXCHANGE)
|
||||
.queuePrefix(LayoutParsingQueueNames.LAYOUT_PARSING_REQUEST_QUEUE_PREFIX)
|
||||
.dlqName(LayoutParsingQueueNames.LAYOUT_PARSING_DLQ)
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void onApplicationReady() {
|
||||
|
||||
System.out.println("application ready invoked");
|
||||
super.initializeQueues();
|
||||
}
|
||||
|
||||
|
||||
@RabbitHandler
|
||||
@RabbitListener(queues = "#{tenantMessagingConfigurationImpl.getTenantCreatedQueueName()}")
|
||||
public void reactToTenantCreation(TenantCreatedEvent tenantCreatedEvent) {
|
||||
|
||||
super.reactToTenantCreation(tenantCreatedEvent);
|
||||
}
|
||||
|
||||
|
||||
@RabbitHandler
|
||||
@RabbitListener(queues = "#{tenantMessagingConfigurationImpl.getTenantDeletedQueueName()}")
|
||||
public void reactToTenantDeletion(TenantResponse tenantResponse) {
|
||||
|
||||
super.reactToTenantDeletion(tenantResponse);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+7
@@ -83,4 +83,11 @@ class SectionIdentifierTest {
|
||||
assertEquals(List.of(4, 1, 2, 4), identifier.getIdentifiers());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFalsePositive111() {
|
||||
SectionIdentifier identifier = SectionIdentifier.fromSearchText("111: Headline");
|
||||
assertEquals(SectionIdentifier.Format.NUMERICAL, identifier.getFormat());
|
||||
assertEquals(1, identifier.level());
|
||||
}
|
||||
|
||||
}
|
||||
+8
-24
@@ -8,12 +8,9 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
@@ -28,6 +25,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.ChangeType;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLog;
|
||||
import com.iqser.red.storage.commons.StorageAutoConfiguration;
|
||||
import com.iqser.red.storage.commons.service.StorageService;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType;
|
||||
@@ -46,8 +44,6 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.ToString;
|
||||
|
||||
|
||||
@Disabled
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@Import(HeadlinesGoldStandardIntegrationTest.RedactionIntegrationTestConfiguration.class)
|
||||
@@ -59,12 +55,6 @@ public class HeadlinesGoldStandardIntegrationTest {
|
||||
@MockBean
|
||||
private RabbitTemplate rabbitTemplate;
|
||||
|
||||
@MockBean
|
||||
private RabbitAdmin rabbitAdmin;
|
||||
|
||||
@MockBean
|
||||
private RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@@ -87,11 +77,9 @@ public class HeadlinesGoldStandardIntegrationTest {
|
||||
"files/headlineTest/S-Metolachlor_RAR_01_Volume_1_2018-09-06_REDACTION_LOG.json"));
|
||||
|
||||
double precision = metrics.stream()
|
||||
.mapToDouble(Metrics::getPrecision).average()
|
||||
.orElse(1.0);
|
||||
.mapToDouble(Metrics::getPrecision).average().orElse(1.0);
|
||||
double recall = metrics.stream()
|
||||
.mapToDouble(Metrics::getRecall).average()
|
||||
.orElse(1.0);
|
||||
.mapToDouble(Metrics::getRecall).average().orElse(1.0);
|
||||
|
||||
System.out.println("Precision is: " + precision + " recall is: " + recall);
|
||||
|
||||
@@ -107,13 +95,10 @@ public class HeadlinesGoldStandardIntegrationTest {
|
||||
ClassPathResource pdfFileResource = new ClassPathResource(filePath);
|
||||
|
||||
Set<Headline> goldStandardHeadlines = new HashSet<>();
|
||||
// var goldStandardLog = objectMapper.readValue(redactionLogResource.getInputStream(), RedactionLog.class);
|
||||
// goldStandardLog.getRedactionLogEntry()
|
||||
// .removeIf(r -> !r.isRedacted() || r.getChanges()
|
||||
// .get(r.getChanges().size() - 1).getType().equals(ChangeType.REMOVED));
|
||||
// goldStandardLog.getRedactionLogEntry()
|
||||
// .forEach(e -> goldStandardHeadlines.add(new Headline(e.getPositions()
|
||||
// .get(0).getPage(), e.getValue())));
|
||||
var goldStandardLog = objectMapper.readValue(redactionLogResource.getInputStream(), RedactionLog.class);
|
||||
goldStandardLog.getRedactionLogEntry().removeIf(r -> !r.isRedacted() || r.getChanges().get(r.getChanges().size() - 1).getType().equals(ChangeType.REMOVED));
|
||||
goldStandardLog.getRedactionLogEntry()
|
||||
.forEach(e -> goldStandardHeadlines.add(new Headline(e.getPositions().get(0).getPage(), e.getValue())));
|
||||
|
||||
Document documentGraph = DocumentGraphFactory.buildDocumentGraph(LayoutParsingType.DOCUMINE,
|
||||
layoutParsingPipeline.parseLayout(LayoutParsingType.DOCUMINE,
|
||||
@@ -128,8 +113,7 @@ public class HeadlinesGoldStandardIntegrationTest {
|
||||
.distinct()
|
||||
.map(headlineNode -> new Headline(headlineNode.getPages()
|
||||
.stream()
|
||||
.findFirst()
|
||||
.get().getNumber(), headlineNode.getTextBlock().getSearchText().stripTrailing()))
|
||||
.findFirst().get().getNumber(), headlineNode.getTextBlock().getSearchText().stripTrailing()))
|
||||
.toList();
|
||||
|
||||
Set<Headline> correct = new HashSet<>();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user