From be2350b289ccbe7f6efda53baa07fef256cdc232 Mon Sep 17 00:00:00 2001 From: yhampe Date: Tue, 12 Dec 2023 15:28:17 +0100 Subject: [PATCH] RED-7964: Prototype Visual Layout Parsing: drawing result from visual layout parsing into viewer document --- .gitlab-ci.yml | 2 +- README.md | 49 +- gradle.properties.kts | 2 +- .../api/data/redaction/DocumentStructure.java | 2 +- .../internal/api/data/taas/RowData.java | 1 + .../api/queue/LayoutParsingFinishedEvent.java | 12 +- .../api/queue/LayoutParsingQueueNames.java | 1 + .../api/queue/LayoutParsingRequest.java | 29 +- .../processor/LayoutParsingPipeline.java | 17 +- .../LayoutParsingStorageService.java | 12 +- .../processor/model/ClassificationPage.java | 1 - .../processor/model/PageContents.java | 1 + .../processor/model/graph/Boundary.java | 2 + .../processor/model/graph/nodes/Document.java | 1 + .../processor/model/graph/nodes/Table.java | 1 + .../graph/textblock/AtomicTextBlock.java | 5 +- .../processor/model/table/TablePageBlock.java | 13 +- .../processor/model/text/SearchableText.java | 1 + .../processor/model/text/TextPageBlock.java | 4 +- .../model/text/TextPositionSequence.java | 1 + .../adapter/ImageServiceResponseAdapter.java | 7 +- .../adapter/VisualLayoutParsingAdapter.java | 52 + .../model/table/ExtractedTable.java | 5 + .../model/table/VisualLayoutParsingBox.java | 20 + .../model/table/VisualLayoutParsingData.java | 20 + .../table/VisualLayoutParsingResponse.java | 23 + .../services/BodyTextFrameService.java | 6 +- .../DividingColumnDetectionService.java | 1 - .../services/GapDetectionService.java | 2 + .../services/GapsAcrossLinesService.java | 5 +- .../services/LineDetectionService.java | 2 +- .../MainBodyTextFrameExtractionService.java | 3 +- .../services/PageContentExtractor.java | 2 +- .../SimplifiedSectionTextService.java | 3 +- .../TaasBlockificationService.java | 32 +- .../DocuMineClassificationService.java | 18 +- .../RedactManagerClassificationService.java | 17 +- .../TaasClassificationService.java | 13 +- .../factory/DocumentGraphFactory.java | 4 +- .../SearchTextWithTextPositionFactory.java | 7 +- .../services/factory/SectionNodeFactory.java | 6 +- .../services/factory/TableNodeFactory.java | 6 +- .../services/factory/TextBlockFactory.java | 2 +- .../services/mapper/DocumentGraphMapper.java | 6 +- .../services/mapper/PropertiesMapper.java | 2 - .../parsing/PDFLinesTextStripper.java | 1 + .../services/parsing/PDFTextStripper.java | 59 +- .../visualization/ViewerDocumentService.java | 16 +- .../processor/utils/MarkedContentUtils.java | 35 +- .../processor/utils/PositionUtils.java | 7 +- .../utils/RectangleTransformations.java | 5 + .../utils/TextPositionOperations.java | 1 - .../utils/TextPositionSequenceComparator.java | 24 +- .../layoutparser/server/Application.java | 2 +- .../server/graph/DocumentDataTests.java | 4 +- .../graph/ExtractMarkedContentTest.java | 27 +- .../server/graph/ViewerDocumentTest.java | 13 +- .../model/TextPositionSequenceTest.java | 1 - .../GapAcrossLinesDetectionServiceTest.java | 5 +- .../InvisibleTableDetectionServiceTest.java | 2 +- ...ainBodyTextFrameExtractionServiceTest.java | 1 - .../services/PageContentExtractorTest.java | 14 +- .../services/PageInformationServiceTest.java | 3 +- .../server/utils/AbstractTest.java | 39 +- .../cv_tables/ScanRotationBorder.TABLES.json | 2021 ++++++++++++++++- 65 files changed, 2463 insertions(+), 238 deletions(-) create mode 100644 layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/adapter/VisualLayoutParsingAdapter.java create mode 100644 layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/ExtractedTable.java create mode 100644 layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/VisualLayoutParsingBox.java create mode 100644 layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/VisualLayoutParsingData.java create mode 100644 layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/VisualLayoutParsingResponse.java diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d054e82..e97257d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,6 +16,6 @@ deploy: reports: dotenv: version.env rules: - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_BRANCH =~ /^release/ - if: $CI_COMMIT_TAG diff --git a/README.md b/README.md index 5a44c75..4e4c09e 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,44 @@ -# PDF Layout Parser Micro-Service: layout-parser +# PDF Layout Parser Micro-Service: layout-parser ## Introduction -The layout-parser micro-service is a powerful tool designed to efficiently extract structured information from PDF documents. Written in Java and utilizing Spring Boot 3, Apache PDFBox, and RabbitMQ, this micro-service excels at parsing PDFs and organizing their content into a meaningful and coherent layout structure. Notably, the layout-parser micro-service distinguishes itself by relying solely on advanced algorithms, rather than machine learning techniques. + +The layout-parser micro-service is a powerful tool designed to efficiently extract structured information from PDF documents. Written in Java and utilizing Spring Boot 3, Apache +PDFBox, and RabbitMQ, this micro-service excels at parsing PDFs and organizing their content into a meaningful and coherent layout structure. Notably, the layout-parser +micro-service distinguishes itself by relying solely on advanced algorithms, rather than machine learning techniques. ### Key Steps in the PDF Layout Parsing Process: * **Text Position Extraction:** -The micro-service leverages Apache PDFBox to extract precise text positions for each individual character within the PDF document. + The micro-service leverages Apache PDFBox to extract precise text positions for each individual character within the PDF document. * **Word Segmentation and Text Block Formation:** -Employing an array of diverse algorithms, the micro-service initially identifies and segments words, creating distinct text blocks. + Employing an array of diverse algorithms, the micro-service initially identifies and segments words, creating distinct text blocks. * **Text Block Classification:** -The segmented text blocks are then subjected to classification algorithms. These algorithms categorize the text blocks based on their content and visual properties, distinguishing between sections, subsections, headlines, paragraphs, images, tables, table cells, headers, and footers. + The segmented text blocks are then subjected to classification algorithms. These algorithms categorize the text blocks based on their content and visual properties, + distinguishing between sections, subsections, headlines, paragraphs, images, tables, table cells, headers, and footers. * **Layout Coherence Establishment:** -The classified text blocks are subsequently orchestrated into a cohesive layout structure. This process involves arranging sections, subsections, paragraphs, images, and other elements in a logical and structured manner. + The classified text blocks are subsequently orchestrated into a cohesive layout structure. This process involves arranging sections, subsections, paragraphs, images, and other + elements in a logical and structured manner. * **Output Generation in Various Formats:** -Once the layout structure is established, the micro-service generates output in multiple formats. These formats are designed for seamless integration with downstream micro-services. The supported formats include JSON, XML, and others, ensuring flexibility in downstream data consumption. + Once the layout structure is established, the micro-service generates output in multiple formats. These formats are designed for seamless integration with downstream + micro-services. The supported formats include JSON, XML, and others, ensuring flexibility in downstream data consumption. ### Optional Enhancements: * **ML-Based Table Extraction:** -For enhanced results, users have the option to incorporate machine learning-based table extraction. This feature can be activated by providing ML-generated results as a JSON file, which are then integrated seamlessly into the layout structure. + For enhanced results, users have the option to incorporate machine learning-based table extraction. This feature can be activated by providing ML-generated results as a JSON + file, which are then integrated seamlessly into the layout structure. * **Image Classification using ML:** -Additionally, for more accurate image classification, users can optionally feed ML-generated image classification results into the micro-service. Similar to the table extraction option, the micro-service processes the pre-parsed results in JSON format, thus optimizing the accuracy of image content identification. - -In conclusion, the layout-parser micro-service is a versatile PDF layout parsing solution crafted entirely around advanced algorithms, without reliance on machine learning. It proficiently extracts text positions, segments content into meaningful blocks, classifies these blocks, arranges them coherently, and outputs structured data for downstream micro-services. Optional integration with ML-generated table extractions and image classifications further enhances its capabilities. - - - - + Additionally, for more accurate image classification, users can optionally feed ML-generated image classification results into the micro-service. Similar to the table extraction + option, the micro-service processes the pre-parsed results in JSON format, thus optimizing the accuracy of image content identification. +In conclusion, the layout-parser micro-service is a versatile PDF layout parsing solution crafted entirely around advanced algorithms, without reliance on machine learning. It +proficiently extracts text positions, segments content into meaningful blocks, classifies these blocks, arranges them coherently, and outputs structured data for downstream +micro-services. Optional integration with ML-generated table extractions and image classifications further enhances its capabilities. ## Installation @@ -49,41 +54,55 @@ To build and test the micro-service, follow these steps: ### Clone the Repository: bash + ``` git clone ssh://git@git.knecon.com:22222/fforesight/layout-parser.git cd layout-parser ``` + ### Build the Project: + Use the following command to build the project using Gradle: ``` gradle clean build ``` + ### Run Tests: + Run the test suite using the following command: + ``` gradle test ``` + ## Building a Custom Docker Image + To create a custom Docker image for the layout-parser micro-service, execute the provided script: ### Ensure Docker is Installed: + Ensure that Docker is installed and running on your system. ### Run the Image Building Script: + Execute the publish-custom-image script in the project directory: ``` ./publish-custom-image ``` + ## Publishing to Internal Maven Repository + To publish the layout-parser micro-service to your internal Maven repository, execute the following command: ``` gradle -Pversion=buildVersion publish ``` + Replace buildVersion with the desired version number. ## Additional Notes + Make sure to configure any necessary application properties before deploying the micro-service. For advanced usage and configurations, refer to Kilian or Dom or preferably the source code. diff --git a/gradle.properties.kts b/gradle.properties.kts index 0b0a3c2..316ee22 100644 --- a/gradle.properties.kts +++ b/gradle.properties.kts @@ -1 +1 @@ -version = 0.1-SNAPSHOT +version = 0.1 - SNAPSHOT diff --git a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java index 6852896..4a36a99 100644 --- a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java +++ b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/redaction/DocumentStructure.java @@ -25,7 +25,6 @@ public class DocumentStructure { @Schema(description = "The root EntryData represents the Document.") EntryData root; - @Schema(description = "Object containing the extra field names, a table has in its properties field.") public static class TableProperties { @@ -56,6 +55,7 @@ public class DocumentStructure { public static final String RECTANGLE_DELIMITER = ";"; + public static Rectangle2D parseRectangle2D(String bBox) { List floats = Arrays.stream(bBox.split(RECTANGLE_DELIMITER)).map(Float::parseFloat).toList(); diff --git a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/taas/RowData.java b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/taas/RowData.java index b7afb80..b3375bb 100644 --- a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/taas/RowData.java +++ b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/data/taas/RowData.java @@ -17,4 +17,5 @@ public class RowData { List cellText; @Schema(description = "The bounding box of this StructureObject. Is always exactly 4 values representing x, y, w, h, where x, y specify the lower left corner.") float[] bBox; + } diff --git a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/queue/LayoutParsingFinishedEvent.java b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/queue/LayoutParsingFinishedEvent.java index 20253bf..103dc3f 100644 --- a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/queue/LayoutParsingFinishedEvent.java +++ b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/queue/LayoutParsingFinishedEvent.java @@ -8,13 +8,9 @@ import lombok.Builder; @Builder @Schema(description = "Object containing information about the layout parsing.") public record LayoutParsingFinishedEvent( - @Schema(description = "General purpose identifier. It is returned exactly the same way it is inserted with the LayoutParsingRequest.") - Map identifier,// - @Schema(description = "The duration of a single layout parsing in ms.") - long duration,// - @Schema(description = "The number of pages of the parsed document.") - int numberOfPages,// - @Schema(description = "A general message. It contains some information useful for a developer, like the paths where the files are stored. Not meant to be machine readable.") - String message) { + @Schema(description = "General purpose identifier. It is returned exactly the same way it is inserted with the LayoutParsingRequest.") Map identifier,// + @Schema(description = "The duration of a single layout parsing in ms.") long duration,// + @Schema(description = "The number of pages of the parsed document.") int numberOfPages,// + @Schema(description = "A general message. It contains some information useful for a developer, like the paths where the files are stored. Not meant to be machine readable.") String message) { } diff --git a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/queue/LayoutParsingQueueNames.java b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/queue/LayoutParsingQueueNames.java index 4aabd62..188c150 100644 --- a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/queue/LayoutParsingQueueNames.java +++ b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/queue/LayoutParsingQueueNames.java @@ -5,4 +5,5 @@ public class LayoutParsingQueueNames { public static final String LAYOUT_PARSING_REQUEST_QUEUE = "layout_parsing_request_queue"; public static final String LAYOUT_PARSING_DLQ = "layout_parsing_dead_letter_queue"; public static final String LAYOUT_PARSING_FINISHED_EVENT_QUEUE = "layout_parsing_response_queue"; + } diff --git a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/queue/LayoutParsingRequest.java b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/queue/LayoutParsingRequest.java index c364bbc..e669b73 100644 --- a/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/queue/LayoutParsingRequest.java +++ b/layoutparser-service/layoutparser-service-internal-api/src/main/java/com/knecon/fforesight/service/layoutparser/internal/api/queue/LayoutParsingRequest.java @@ -17,24 +17,35 @@ public record LayoutParsingRequest( Map identifier, @Schema(description = "Path to the original PDF file.")// - @NonNull String originFileStorageId,// + @NonNull String originFileStorageId, +// + + @Schema(description = "Optional Path to the the visual layout parsing service file") Optional visualLayoutParsingFileId, @Schema(description = "Optional Path to the table extraction file.")// - Optional tablesFileStorageId,// + Optional tablesFileStorageId, +// @Schema(description = "Optional Path to the image classification file.")// - Optional imagesFileStorageId,// + Optional imagesFileStorageId, +// @Schema(description = "Path where the Document Structure File will be stored.")// - @NonNull String structureFileStorageId,// + @NonNull String structureFileStorageId, +// @Schema(description = "Path where the Research Data File will be stored.")// - String researchDocumentStorageId,// + String researchDocumentStorageId, +// @Schema(description = "Path where the Document Text File will be stored.")// - @NonNull String textBlockFileStorageId,// + @NonNull String textBlockFileStorageId, +// @Schema(description = "Path where the Document Positions File will be stored.")// - @NonNull String positionBlockFileStorageId,// + @NonNull String positionBlockFileStorageId, +// @Schema(description = "Path where the Document Pages File will be stored.")// - @NonNull String pageFileStorageId,// + @NonNull String pageFileStorageId, +// @Schema(description = "Path where the Simplified Text File will be stored.")// - @NonNull String simplifiedTextStorageId,// + @NonNull String simplifiedTextStorageId, +// @Schema(description = "Path where the Viewer Document PDF will be stored.")// @NonNull String viewerDocumentStorageId) { diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java index a02f627..10a66db 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingPipeline.java @@ -30,9 +30,11 @@ import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageB import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence; 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; import com.knecon.fforesight.service.layoutparser.processor.python_api.model.image.ImageServiceResponse; 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; @@ -71,6 +73,7 @@ public class LayoutParsingPipeline { private final BodyTextFrameService bodyTextFrameService; private final RulingCleaningService rulingCleaningService; private final TableExtractionService tableExtractionService; + private final VisualLayoutParsingAdapter visualLayoutParsingAdapter; private final TaasBlockificationService taasBlockificationService; private final DocuMineBlockificationService docuMineBlockificationService; private final RedactManagerBlockificationService redactManagerBlockificationService; @@ -92,6 +95,11 @@ public class LayoutParsingPipeline { tableServiceResponse = layoutParsingStorageService.getTablesFile(layoutParsingRequest.tablesFileStorageId().get()); } + VisualLayoutParsingResponse visualLayoutParsingResponse = new VisualLayoutParsingResponse(); + if (layoutParsingRequest.visualLayoutParsingFileId().isPresent()) { + visualLayoutParsingResponse = layoutParsingStorageService.getExtractedTablesFile(layoutParsingRequest.visualLayoutParsingFileId().get()); + } + ClassificationDocument classificationDocument = parseLayout(layoutParsingRequest.layoutParsingType(), originDocument, imageServiceResponse, tableServiceResponse); Document documentGraph = DocumentGraphFactory.buildDocumentGraph(classificationDocument); @@ -100,8 +108,9 @@ public class LayoutParsingPipeline { layoutParsingStorageService.storeDocumentData(layoutParsingRequest, DocumentDataMapper.toDocumentData(documentGraph)); layoutParsingStorageService.storeSimplifiedText(layoutParsingRequest, simplifiedSectionTextService.toSimplifiedText(documentGraph)); + Map> extractedTableCells = visualLayoutParsingAdapter.buildExtractedTablesPerPage(visualLayoutParsingResponse); try (var out = new ByteArrayOutputStream()) { - viewerDocumentService.createViewerDocument(originDocument, documentGraph, out, false); + viewerDocumentService.createViewerDocument(originDocument, documentGraph, out, extractedTableCells, false); layoutParsingStorageService.storeViewerDocument(layoutParsingRequest, out); } @@ -244,9 +253,9 @@ public class LayoutParsingPipeline { private void increaseDocumentStatistics(ClassificationPage classificationPage, ClassificationDocument document) { - if (!classificationPage.isLandscape()) { - document.getFontSizeCounter().addAll(classificationPage.getFontSizeCounter().getCountPerValue()); - } + if (!classificationPage.isLandscape()) { + document.getFontSizeCounter().addAll(classificationPage.getFontSizeCounter().getCountPerValue()); + } document.getFontCounter().addAll(classificationPage.getFontCounter().getCountPerValue()); document.getTextHeightCounter().addAll(classificationPage.getTextHeightCounter().getCountPerValue()); document.getFontStyleCounter().addAll(classificationPage.getFontStyleCounter().getCountPerValue()); diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingStorageService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingStorageService.java index 6e35cbe..d810967 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingStorageService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/LayoutParsingStorageService.java @@ -24,6 +24,7 @@ import com.knecon.fforesight.service.layoutparser.internal.api.data.taas.Researc import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingRequest; import com.knecon.fforesight.service.layoutparser.processor.python_api.model.image.ImageServiceResponse; import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableServiceResponse; +import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.VisualLayoutParsingResponse; import com.knecon.fforesight.tenantcommons.TenantContext; import lombok.RequiredArgsConstructor; @@ -74,6 +75,16 @@ public class LayoutParsingStorageService { } + public VisualLayoutParsingResponse getExtractedTablesFile(String storageId) throws IOException { + + try (InputStream inputStream = getObject(storageId)) { + VisualLayoutParsingResponse visualLayoutParsingResponse = objectMapper.readValue(inputStream, VisualLayoutParsingResponse.class); + inputStream.close(); + return visualLayoutParsingResponse; + } + } + + public void storeDocumentData(LayoutParsingRequest layoutParsingRequest, DocumentData documentData) { storageService.storeJSONObject(TenantContext.getTenantId(), layoutParsingRequest.structureFileStorageId(), documentData.getDocumentStructure()); @@ -83,7 +94,6 @@ public class LayoutParsingStorageService { } - public void storeResearchDocumentData(LayoutParsingRequest layoutParsingRequest, ResearchDocumentData researchDocumentData) { storageService.storeJSONObject(TenantContext.getTenantId(), layoutParsingRequest.researchDocumentStorageId(), researchDocumentData); diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/ClassificationPage.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/ClassificationPage.java index a654636..25c49f2 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/ClassificationPage.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/ClassificationPage.java @@ -14,7 +14,6 @@ import com.knecon.fforesight.service.layoutparser.processor.model.text.StringFre import lombok.Data; import lombok.NonNull; import lombok.RequiredArgsConstructor; -import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDMarkedContent; @Data @RequiredArgsConstructor diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/PageContents.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/PageContents.java index f9d00ef..5f48353 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/PageContents.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/PageContents.java @@ -19,4 +19,5 @@ public class PageContents { Rectangle2D cropBox; Rectangle2D mediaBox; List rulings; + } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/Boundary.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/Boundary.java index 28de929..da0a88b 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/Boundary.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/Boundary.java @@ -108,11 +108,13 @@ public class Boundary implements Comparable { return splitBoundaries; } + public IntStream intStream() { return IntStream.range(start, end); } + public static Boundary merge(Collection boundaries) { int minStart = boundaries.stream().mapToInt(Boundary::start).min().orElseThrow(IllegalArgumentException::new); diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Document.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Document.java index 0df92c2..e8e43d13 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Document.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Document.java @@ -105,6 +105,7 @@ public class Document implements GenericSemanticNode { return streamAllSubNodes().collect(Collectors.groupingBy(SemanticNode::getType, Collectors.counting())); } + @Override public String toString() { diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java index d62b4cf..7855db0 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/nodes/Table.java @@ -207,6 +207,7 @@ public class Table implements SemanticNode { return IntStream.range(0, numberOfCols).boxed().map(col -> getCell(row, col)); } + /** * Streams all TableCells row-wise and filters them with header == true. * diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/textblock/AtomicTextBlock.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/textblock/AtomicTextBlock.java index 33d9427..ecb0679 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/textblock/AtomicTextBlock.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/graph/textblock/AtomicTextBlock.java @@ -109,10 +109,7 @@ public class AtomicTextBlock implements TextBlock { } - public static AtomicTextBlock fromAtomicTextBlockData(DocumentTextData documentTextData, - DocumentPositionData documentPositionData, - SemanticNode parent, - Page page) { + public static AtomicTextBlock fromAtomicTextBlockData(DocumentTextData documentTextData, DocumentPositionData documentPositionData, SemanticNode parent, Page page) { return AtomicTextBlock.builder() .id(documentTextData.getId()) diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java index 1295424..b874f2d 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/table/TablePageBlock.java @@ -1,14 +1,12 @@ package com.knecon.fforesight.service.layoutparser.processor.model.table; import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeMap; -import java.util.stream.Collectors; import com.knecon.fforesight.service.layoutparser.processor.model.AbstractPageBlock; import com.knecon.fforesight.service.layoutparser.processor.model.PageBlockType; @@ -50,6 +48,7 @@ public class TablePageBlock extends AbstractPageBlock { return getColCount() == 0 || getRowCount() == 0; } + public List> getRows() { if (rows == null) { @@ -276,21 +275,17 @@ public class TablePageBlock extends AbstractPageBlock { } - public boolean intersects(Cell cell1, Cell cell2) { + if (cell1.getHeight() <= 0 || cell2.getHeight() <= 0) { return false; } double x0 = cell1.getX() + 2; double y0 = cell1.getY() + 2; - return (cell2.x + cell2.width > x0 && - cell2.y + cell2.height > y0 && - cell2.x < x0 + cell1.getWidth() -2 && - cell2.y < y0 + cell1.getHeight() -2); + return (cell2.x + cell2.width > x0 && cell2.y + cell2.height > y0 && cell2.x < x0 + cell1.getWidth() - 2 && cell2.y < y0 + cell1.getHeight() - 2); } - @Override public String getText() { @@ -328,8 +323,6 @@ public class TablePageBlock extends AbstractPageBlock { } - - public String getTextAsHtml() { StringBuilder sb = new StringBuilder(); diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/SearchableText.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/SearchableText.java index c0ef4e3..d793e73 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/SearchableText.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/SearchableText.java @@ -2,6 +2,7 @@ package com.knecon.fforesight.service.layoutparser.processor.model.text; import java.util.ArrayList; import java.util.List; + import com.knecon.fforesight.service.layoutparser.processor.utils.TextNormalizationUtilities; import lombok.Getter; diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/TextPageBlock.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/TextPageBlock.java index 0442af6..ac2ae98 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/TextPageBlock.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/TextPageBlock.java @@ -73,7 +73,7 @@ public class TextPageBlock extends AbstractPageBlock { return sequences.get(0).getPageWidth(); } - + public static TextPageBlock merge(List textBlocksToMerge) { @@ -82,6 +82,7 @@ public class TextPageBlock extends AbstractPageBlock { return fromTextPositionSequences(sequences); } + public static TextPageBlock fromTextPositionSequences(List wordBlockList) { TextPageBlock textBlock = null; @@ -133,7 +134,6 @@ public class TextPageBlock extends AbstractPageBlock { } - /** * Returns the minX value in pdf coordinate system. * Note: This needs to use Pdf Coordinate System where {0,0} rotated with the page rotation. diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/TextPositionSequence.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/TextPositionSequence.java index 7b776dc..b21076f 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/TextPositionSequence.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/model/text/TextPositionSequence.java @@ -234,6 +234,7 @@ public class TextPositionSequence implements CharSequence { @JsonIgnore @JsonAttribute(ignore = true) public String getFontStyle() { + if (textPositions.get(0).getFontName() == null) { return "standard"; } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/adapter/ImageServiceResponseAdapter.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/adapter/ImageServiceResponseAdapter.java index 55c345c..5b1a61d 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/adapter/ImageServiceResponseAdapter.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/adapter/ImageServiceResponseAdapter.java @@ -9,10 +9,10 @@ import java.util.Map; import org.springframework.stereotype.Service; -import com.knecon.fforesight.service.layoutparser.processor.python_api.model.image.ImageServiceResponse; import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationPage; -import com.knecon.fforesight.service.layoutparser.processor.model.image.ClassifiedImage; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.ImageType; +import com.knecon.fforesight.service.layoutparser.processor.model.image.ClassifiedImage; +import com.knecon.fforesight.service.layoutparser.processor.python_api.model.image.ImageServiceResponse; import lombok.RequiredArgsConstructor; @@ -20,8 +20,7 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class ImageServiceResponseAdapter { - - public Map> buildClassifiedImagesPerPage(ImageServiceResponse imageServiceResponse ) { + public Map> buildClassifiedImagesPerPage(ImageServiceResponse imageServiceResponse) { Map> images = new HashMap<>(); imageServiceResponse.getData().forEach(imageMetadata -> { diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/adapter/VisualLayoutParsingAdapter.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/adapter/VisualLayoutParsingAdapter.java new file mode 100644 index 0000000..843fdbe --- /dev/null +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/adapter/VisualLayoutParsingAdapter.java @@ -0,0 +1,52 @@ +package com.knecon.fforesight.service.layoutparser.processor.python_api.adapter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; + +import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableCells; +import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.VisualLayoutParsingBox; +import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.VisualLayoutParsingResponse; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class VisualLayoutParsingAdapter { + + public Map> buildExtractedTablesPerPage(VisualLayoutParsingResponse visualLayoutParsingResponse) { + + Map> tableCells = new HashMap<>(); + visualLayoutParsingResponse.getData() + .forEach(tableData -> tableCells.computeIfAbsent(tableData.getPage_idx(), tableCell -> new ArrayList<>()).addAll(convertTableCells(tableData.getBoxes()))); + + return tableCells; + + } + + + public List convertTableCells(List tableObjects) { + + List parsedTableCells = new ArrayList<>(); + + tableObjects.stream().forEach(t -> { + if(t.getProbability() > 0.9) { + TableCells tableCells = new TableCells(); + tableCells.setX0(t.getBox().getX1()); + tableCells.setX1(t.getBox().getX2()); + tableCells.setY0(t.getBox().getY1()); + tableCells.setY1(t.getBox().getY2()); + tableCells.setWidth(tableCells.getX1() - tableCells.getX0()); + tableCells.setHeight(tableCells.getY1() - tableCells.getY0()); + parsedTableCells.add(tableCells); + } + }); + + return parsedTableCells; + + } + +} diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/ExtractedTable.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/ExtractedTable.java new file mode 100644 index 0000000..873a5d2 --- /dev/null +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/ExtractedTable.java @@ -0,0 +1,5 @@ +package com.knecon.fforesight.service.layoutparser.processor.python_api.model.table; + +public class ExtractedTable { + +} diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/VisualLayoutParsingBox.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/VisualLayoutParsingBox.java new file mode 100644 index 0000000..bbd904a --- /dev/null +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/VisualLayoutParsingBox.java @@ -0,0 +1,20 @@ +package com.knecon.fforesight.service.layoutparser.processor.python_api.model.table; + +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisualLayoutParsingBox { + + private VisualLayoutParsingBoxValue box; + private String label; + private float probability; + +} diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/VisualLayoutParsingData.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/VisualLayoutParsingData.java new file mode 100644 index 0000000..d10daf2 --- /dev/null +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/VisualLayoutParsingData.java @@ -0,0 +1,20 @@ +package com.knecon.fforesight.service.layoutparser.processor.python_api.model.table; + +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisualLayoutParsingData { + + private int page_idx; + + private List boxes; + +} diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/VisualLayoutParsingResponse.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/VisualLayoutParsingResponse.java new file mode 100644 index 0000000..57bd673 --- /dev/null +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/python_api/model/table/VisualLayoutParsingResponse.java @@ -0,0 +1,23 @@ +package com.knecon.fforesight.service.layoutparser.processor.python_api.model.table; + +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VisualLayoutParsingResponse { + + private String dossierId; + private String fileId; + private String targetFileExtension; + private String responseFileExtension; + private String X_TENANT_ID; + private List data; + +} diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/BodyTextFrameService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/BodyTextFrameService.java index 5942443..63ee3c3 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/BodyTextFrameService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/BodyTextFrameService.java @@ -25,6 +25,7 @@ public class BodyTextFrameService { private static final float RULING_HEIGHT_THRESHOLD = 0.15f; // multiplied with page height. Header/Footer Rulings must be within that border of the page. private static final float RULING_WIDTH_THRESHOLD = 0.75f; // multiplied with page width. Header/Footer Rulings must be at least that wide. + public void setBodyTextFrames(ClassificationDocument classificationDocument, LayoutParsingType layoutParsingType) { Rectangle bodyTextFrame = calculateBodyTextFrame(classificationDocument.getPages(), classificationDocument.getFontSizeCounter(), false, layoutParsingType); @@ -155,8 +156,9 @@ public class BodyTextFrameService { continue; } - if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.HEADER) - || MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.FOOTER)) { + if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.HEADER) || MarkedContentUtils.intersects(textBlock, + page.getMarkedContentBboxPerType(), + MarkedContentUtils.FOOTER)) { continue; } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/DividingColumnDetectionService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/DividingColumnDetectionService.java index 7c062bc..f3e70b2 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/DividingColumnDetectionService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/DividingColumnDetectionService.java @@ -22,7 +22,6 @@ public class DividingColumnDetectionService { public List detectColumns(PageContents pageContents) { - if (pageContents.getSortedTextPositionSequences().size() < 2) { return List.of(pageContents.getCropBox()); } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/GapDetectionService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/GapDetectionService.java index ac7db1d..5200b52 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/GapDetectionService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/GapDetectionService.java @@ -72,11 +72,13 @@ public class GapDetectionService { return mirrorY(RectangleTransformations.toRectangle2D(textPosition.getRectangle())); } + private static Rectangle2D mirrorY(Rectangle2D rectangle2D) { return new Rectangle2D.Double(rectangle2D.getX(), Math.min(rectangle2D.getMinY(), rectangle2D.getMaxY()), rectangle2D.getWidth(), Math.abs(rectangle2D.getHeight())); } + private static void addGapToLine(Rectangle2D currentTextPosition, Rectangle2D previousTextPosition, XGapsContext context) { context.gapsInCurrentLine.add(new Rectangle2D.Double(previousTextPosition.getMaxX(), diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/GapsAcrossLinesService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/GapsAcrossLinesService.java index 94bcce2..6f80979 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/GapsAcrossLinesService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/GapsAcrossLinesService.java @@ -6,7 +6,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.stream.Stream; -import com.iqser.red.commons.jackson.ObjectMapperFactory; import com.knecon.fforesight.service.layoutparser.processor.model.GapInformation; @@ -51,7 +50,9 @@ public class GapsAcrossLinesService { } return columnFactory.outputGaps.stream() - .filter(gapAcrossLines -> columnFactory.outputGaps.stream().filter(gapAcrossLines::intersectsX).noneMatch(gapAcrossLines1 -> gapAcrossLines1.lineCount > gapAcrossLines.lineCount)) + .filter(gapAcrossLines -> columnFactory.outputGaps.stream() + .filter(gapAcrossLines::intersectsX) + .noneMatch(gapAcrossLines1 -> gapAcrossLines1.lineCount > gapAcrossLines.lineCount)) .filter(gapAcrossLines -> Math.abs(gapAcrossLines.rectangle2D.getMinX() - mainBodyTextFrame.getMinX()) > DISTANCE_TO_BORDER_THRESHOLD) .filter(gapAcrossLines -> Math.abs(gapAcrossLines.rectangle2D.getMaxX() - mainBodyTextFrame.getMaxX()) > DISTANCE_TO_BORDER_THRESHOLD) .map(GapAcrossLines::getRectangle2D) diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/LineDetectionService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/LineDetectionService.java index 8b14767..bf1faf5 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/LineDetectionService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/LineDetectionService.java @@ -6,8 +6,8 @@ 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.utils.RectangleTransformations; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/MainBodyTextFrameExtractionService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/MainBodyTextFrameExtractionService.java index 0cac3ee..6dd9928 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/MainBodyTextFrameExtractionService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/MainBodyTextFrameExtractionService.java @@ -16,8 +16,7 @@ public class MainBodyTextFrameExtractionService { public Rectangle2D calculateMainBodyTextFrame(LineInformation lineInformation) { - Rectangle2D mainBodyTextFrame = lineInformation.getLineBBox().stream() - .collect(RectangleTransformations.collectBBox()); + Rectangle2D mainBodyTextFrame = lineInformation.getLineBBox().stream().collect(RectangleTransformations.collectBBox()); return RectangleTransformations.pad(mainBodyTextFrame, mainBodyTextFrame.getWidth() * TEXT_FRAME_PAD_WIDTH, mainBodyTextFrame.getHeight() * TEXT_FRAME_PAD_HEIGHT); } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/PageContentExtractor.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/PageContentExtractor.java index dde3b94..ff5fb01 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/PageContentExtractor.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/PageContentExtractor.java @@ -52,7 +52,7 @@ public class PageContentExtractor { stripper.getRulings())); } } - + return textPositionSequencesPerPage; } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/SimplifiedSectionTextService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/SimplifiedSectionTextService.java index eb1e824..867b64d 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/SimplifiedSectionTextService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/SimplifiedSectionTextService.java @@ -5,9 +5,9 @@ import java.util.List; import org.springframework.stereotype.Service; import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.SimplifiedSectionText; +import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.SimplifiedText; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Section; -import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.SimplifiedText; @Service public class SimplifiedSectionTextService { @@ -23,4 +23,5 @@ public class SimplifiedSectionTextService { return SimplifiedSectionText.builder().sectionNumber(section.getTreeId().get(0)).text(section.getTextBlock().getSearchText()).build(); } + } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/blockification/TaasBlockificationService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/blockification/TaasBlockificationService.java index 287d2ba..ea2f909 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/blockification/TaasBlockificationService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/blockification/TaasBlockificationService.java @@ -1,9 +1,20 @@ package com.knecon.fforesight.service.layoutparser.processor.services.blockification; - // TODO: figure out, why this fails the build // import static com.knecon.fforesight.service.layoutparser.processor.services.factory.SearchTextWithTextPositionFactory.HEIGHT_PADDING; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import org.springframework.stereotype.Service; + import com.knecon.fforesight.service.layoutparser.processor.model.AbstractPageBlock; import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationPage; import com.knecon.fforesight.service.layoutparser.processor.model.Orientation; @@ -11,12 +22,6 @@ import com.knecon.fforesight.service.layoutparser.processor.model.table.Ruling; 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.utils.RulingTextDirAdjustUtil; -import org.springframework.stereotype.Service; - -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Stream; @Service @SuppressWarnings("all") @@ -83,13 +88,13 @@ public class TaasBlockificationService { continue; } - Matcher listIdentifierPattern = listIdentifier.matcher(currentTextBlock.getText()); boolean isListIdentifier = listIdentifierPattern.find(); boolean yGap = Math.abs(currentTextBlock.getPdfMaxY() - previousTextBlock.getPdfMinY()) < previousTextBlock.getMostPopularWordHeight() * Y_GAP_SPLIT_HEIGHT_MODIFIER; - boolean sameFont = previousTextBlock.getMostPopularWordFont().equals(currentTextBlock.getMostPopularWordFont()) && previousTextBlock.getMostPopularWordFontSize() == currentTextBlock.getMostPopularWordFontSize(); + boolean sameFont = previousTextBlock.getMostPopularWordFont() + .equals(currentTextBlock.getMostPopularWordFont()) && previousTextBlock.getMostPopularWordFontSize() == currentTextBlock.getMostPopularWordFontSize(); // boolean yGap = previousTextBlock != null && currentTextBlock.getMinYDirAdj() - maxY > Math.min(word.getHeight(), prev.getHeight()) * Y_GAP_SPLIT_HEIGHT_MODIFIER; boolean alignsXRight = Math.abs(currentTextBlock.getPdfMaxX() - previousTextBlock.getPdfMaxX()) < X_ALIGNMENT_THRESHOLD; @@ -119,8 +124,9 @@ public class TaasBlockificationService { } alreadyMerged.add(textPageBlock); textBlocksToMerge.add(Stream.concat(Stream.of(textPageBlock), - textPageBlocks.stream().filter(textPageBlock2 -> textPageBlock.almostIntersects(textPageBlock2, INTERSECTS_Y_THRESHOLD, 0) && !alreadyMerged.contains(textPageBlock2)).peek(alreadyMerged::add)) - .toList()); + textPageBlocks.stream() + .filter(textPageBlock2 -> textPageBlock.almostIntersects(textPageBlock2, INTERSECTS_Y_THRESHOLD, 0) && !alreadyMerged.contains(textPageBlock2)) + .peek(alreadyMerged::add)).toList()); } return textBlocksToMerge.stream().map(TextPageBlock::merge).toList(); } @@ -163,8 +169,7 @@ public class TaasBlockificationService { while (itty.hasNext()) { TextPageBlock block = (TextPageBlock) itty.next(); - if (previous != null && previous.getOrientation().equals(Orientation.LEFT) && block.getOrientation().equals(Orientation.LEFT) && equalsWithThreshold( - block.getMaxY(), + if (previous != null && previous.getOrientation().equals(Orientation.LEFT) && block.getOrientation().equals(Orientation.LEFT) && equalsWithThreshold(block.getMaxY(), previous.getMaxY()) || previous != null && previous.getOrientation().equals(Orientation.LEFT) && block.getOrientation() .equals(Orientation.RIGHT) && equalsWithThreshold(block.getMaxY(), previous.getMaxY())) { previous.add(block); @@ -189,7 +194,6 @@ public class TaasBlockificationService { TextPositionSequence prev = null; // TODO: make static final constant - boolean wasSplitted = false; Float splitX1 = null; for (TextPositionSequence word : textPositions) { diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/classification/DocuMineClassificationService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/classification/DocuMineClassificationService.java index d622fc8..0df4dbe 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/classification/DocuMineClassificationService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/classification/DocuMineClassificationService.java @@ -5,7 +5,6 @@ import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.knecon.fforesight.service.layoutparser.processor.utils.MarkedContentUtils; import org.springframework.stereotype.Service; import com.knecon.fforesight.service.layoutparser.processor.model.AbstractPageBlock; @@ -13,6 +12,7 @@ import com.knecon.fforesight.service.layoutparser.processor.model.Classification 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.text.TextPageBlock; +import com.knecon.fforesight.service.layoutparser.processor.utils.MarkedContentUtils; import com.knecon.fforesight.service.layoutparser.processor.utils.PositionUtils; import lombok.RequiredArgsConstructor; @@ -63,16 +63,16 @@ public class DocuMineClassificationService { textBlock.setClassification(PageBlockType.OTHER); return; } - if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.HEADER) - || PositionUtils.isOverBodyTextFrame(bodyTextFrame, textBlock, page.getRotation()) && (document.getFontSizeCounter() - .getMostPopular() == null || textBlock.getHighestFontSize() <= document.getFontSizeCounter().getMostPopular()) - ) { + if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.HEADER) || PositionUtils.isOverBodyTextFrame(bodyTextFrame, + textBlock, + page.getRotation()) && (document.getFontSizeCounter().getMostPopular() == null || textBlock.getHighestFontSize() <= document.getFontSizeCounter() + .getMostPopular())) { textBlock.setClassification(PageBlockType.HEADER); - } else if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.FOOTER) - || PositionUtils.isUnderBodyTextFrame(bodyTextFrame, textBlock, page.getRotation()) && (document.getFontSizeCounter() - .getMostPopular() == null || textBlock.getHighestFontSize() <= document.getFontSizeCounter().getMostPopular()) - ) { + } else if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.FOOTER) || PositionUtils.isUnderBodyTextFrame(bodyTextFrame, + textBlock, + page.getRotation()) && (document.getFontSizeCounter().getMostPopular() == null || textBlock.getHighestFontSize() <= document.getFontSizeCounter() + .getMostPopular())) { textBlock.setClassification(PageBlockType.FOOTER); } else if (page.getPageNumber() == 1 && (PositionUtils.getHeightDifferenceBetweenChunkWordAndDocumentWord(textBlock, document.getTextHeightCounter().getMostPopular()) > 2.5 && textBlock.getHighestFontSize() > document.getFontSizeCounter().getMostPopular() || page.getTextBlocks() diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/classification/RedactManagerClassificationService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/classification/RedactManagerClassificationService.java index 3e90c57..9ea2c95 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/classification/RedactManagerClassificationService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/classification/RedactManagerClassificationService.java @@ -3,7 +3,6 @@ package com.knecon.fforesight.service.layoutparser.processor.services.classifica import java.util.List; import java.util.regex.Pattern; -import com.knecon.fforesight.service.layoutparser.processor.utils.MarkedContentUtils; import org.springframework.stereotype.Service; import com.knecon.fforesight.service.layoutparser.processor.model.AbstractPageBlock; @@ -11,6 +10,7 @@ import com.knecon.fforesight.service.layoutparser.processor.model.Classification 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.text.TextPageBlock; +import com.knecon.fforesight.service.layoutparser.processor.utils.MarkedContentUtils; import com.knecon.fforesight.service.layoutparser.processor.utils.PositionUtils; import lombok.RequiredArgsConstructor; @@ -21,7 +21,6 @@ import lombok.extern.slf4j.Slf4j; @RequiredArgsConstructor public class RedactManagerClassificationService { - public void classifyDocument(ClassificationDocument document) { List headlineFontSizes = document.getFontSizeCounter().getHighterThanMostPopular(); @@ -52,14 +51,16 @@ public class RedactManagerClassificationService { textBlock.setClassification(PageBlockType.OTHER); return; } - if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.HEADER) - || PositionUtils.isOverBodyTextFrame(bodyTextFrame, textBlock, page.getRotation()) && (document.getFontSizeCounter() - .getMostPopular() == null || textBlock.getHighestFontSize() <= document.getFontSizeCounter().getMostPopular())) { + if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.HEADER) || PositionUtils.isOverBodyTextFrame(bodyTextFrame, + textBlock, + page.getRotation()) && (document.getFontSizeCounter().getMostPopular() == null || textBlock.getHighestFontSize() <= document.getFontSizeCounter() + .getMostPopular())) { textBlock.setClassification(PageBlockType.HEADER); - } else if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.FOOTER) - || PositionUtils.isUnderBodyTextFrame(bodyTextFrame, textBlock, page.getRotation()) && (document.getFontSizeCounter() - .getMostPopular() == null || textBlock.getHighestFontSize() <= document.getFontSizeCounter().getMostPopular())) { + } else if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.FOOTER) || PositionUtils.isUnderBodyTextFrame(bodyTextFrame, + textBlock, + page.getRotation()) && (document.getFontSizeCounter().getMostPopular() == null || textBlock.getHighestFontSize() <= document.getFontSizeCounter() + .getMostPopular())) { textBlock.setClassification(PageBlockType.FOOTER); } else if (page.getPageNumber() == 1 && (PositionUtils.getHeightDifferenceBetweenChunkWordAndDocumentWord(textBlock, document.getTextHeightCounter().getMostPopular()) > 2.5 && textBlock.getHighestFontSize() > document.getFontSizeCounter().getMostPopular() || page.getTextBlocks() diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/classification/TaasClassificationService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/classification/TaasClassificationService.java index 7a91be1..78a5339 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/classification/TaasClassificationService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/classification/TaasClassificationService.java @@ -3,7 +3,6 @@ package com.knecon.fforesight.service.layoutparser.processor.services.classifica import java.util.List; import java.util.regex.Pattern; -import com.knecon.fforesight.service.layoutparser.processor.utils.MarkedContentUtils; import org.springframework.stereotype.Service; import com.knecon.fforesight.service.layoutparser.processor.model.AbstractPageBlock; @@ -12,6 +11,7 @@ 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.text.TextPageBlock; import com.knecon.fforesight.service.layoutparser.processor.services.BodyTextFrameService; +import com.knecon.fforesight.service.layoutparser.processor.utils.MarkedContentUtils; import com.knecon.fforesight.service.layoutparser.processor.utils.PositionUtils; import lombok.RequiredArgsConstructor; @@ -27,7 +27,6 @@ public class TaasClassificationService { public void classifyDocument(ClassificationDocument document) { - List headlineFontSizes = document.getFontSizeCounter().getHighterThanMostPopular(); log.debug("Document FontSize counters are: {}", document.getFontSizeCounter().getCountPerValue()); @@ -57,11 +56,13 @@ public class TaasClassificationService { textBlock.setClassification(PageBlockType.OTHER); return; } - if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.HEADER) - || PositionUtils.isOverBodyTextFrame(bodyTextFrame, textBlock, page.getRotation())) { + if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.HEADER) || PositionUtils.isOverBodyTextFrame(bodyTextFrame, + textBlock, + page.getRotation())) { textBlock.setClassification(PageBlockType.HEADER); - } else if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.FOOTER) - || PositionUtils.isUnderBodyTextFrame(bodyTextFrame, textBlock, page.getRotation())) { + } else if (MarkedContentUtils.intersects(textBlock, page.getMarkedContentBboxPerType(), MarkedContentUtils.FOOTER) || PositionUtils.isUnderBodyTextFrame(bodyTextFrame, + textBlock, + page.getRotation())) { textBlock.setClassification(PageBlockType.FOOTER); } else if (page.getPageNumber() == 1 && (PositionUtils.getHeightDifferenceBetweenChunkWordAndDocumentWord(textBlock, document.getTextHeightCounter().getMostPopular()) > 2.5 && textBlock.getHighestFontSize() > document.getFontSizeCounter().getMostPopular() || page.getTextBlocks() diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/DocumentGraphFactory.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/DocumentGraphFactory.java index 6bde310..a9061c2 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/DocumentGraphFactory.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/DocumentGraphFactory.java @@ -18,8 +18,6 @@ import com.knecon.fforesight.service.layoutparser.processor.model.Classification import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationFooter; import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationHeader; import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationPage; -import com.knecon.fforesight.service.layoutparser.processor.model.image.ClassifiedImage; -import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock; 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.Footer; @@ -31,6 +29,8 @@ import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Pa import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Paragraph; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Section; import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.AtomicTextBlock; +import com.knecon.fforesight.service.layoutparser.processor.model.image.ClassifiedImage; +import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock; import com.knecon.fforesight.service.layoutparser.processor.utils.IdBuilder; import com.knecon.fforesight.service.layoutparser.processor.utils.TextPositionOperations; diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/SearchTextWithTextPositionFactory.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/SearchTextWithTextPositionFactory.java index 7ebc737..4670b5e 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/SearchTextWithTextPositionFactory.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/SearchTextWithTextPositionFactory.java @@ -8,10 +8,10 @@ import java.util.List; import java.util.Locale; import java.util.Objects; +import com.knecon.fforesight.service.layoutparser.processor.model.graph.Boundary; 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.graph.Boundary; import lombok.experimental.UtilityClass; @@ -110,6 +110,7 @@ public class SearchTextWithTextPositionFactory { return context.stringIdx - context.lastHyphenIdx < MAX_HYPHEN_LINEBREAK_DISTANCE; } + private static List mergeToBoundaries(List integers) { if (integers.isEmpty()) { @@ -125,8 +126,9 @@ public class SearchTextWithTextPositionFactory { } end = current + 1; } - if (boundaries.isEmpty()) + if (boundaries.isEmpty()) { boundaries.add(new Boundary(start, end)); + } return boundaries; } @@ -138,6 +140,7 @@ public class SearchTextWithTextPositionFactory { } } + private boolean isLineBreak(RedTextPosition currentTextPosition, RedTextPosition previousTextPosition) { return Objects.equals(currentTextPosition.getUnicode(), "\n") || isDeltaYLargerThanTextHeight(currentTextPosition, previousTextPosition); diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/SectionNodeFactory.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/SectionNodeFactory.java index 7bd82e2..4face6a 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/SectionNodeFactory.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/SectionNodeFactory.java @@ -11,12 +11,12 @@ import java.util.Map; import java.util.Set; import com.knecon.fforesight.service.layoutparser.processor.model.AbstractPageBlock; -import com.knecon.fforesight.service.layoutparser.processor.model.image.ClassifiedImage; -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.graph.nodes.GenericSemanticNode; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Page; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Section; +import com.knecon.fforesight.service.layoutparser.processor.model.image.ClassifiedImage; +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.utils.TableMergingUtility; import lombok.experimental.UtilityClass; diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/TableNodeFactory.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/TableNodeFactory.java index c00edd1..8d8a660 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/TableNodeFactory.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/TableNodeFactory.java @@ -8,15 +8,15 @@ import java.util.Set; import java.util.stream.Collectors; import com.knecon.fforesight.service.layoutparser.processor.model.AbstractPageBlock; -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.graph.nodes.GenericSemanticNode; 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.nodes.Table; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.TableCell; 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.utils.TextPositionOperations; import lombok.experimental.UtilityClass; diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/TextBlockFactory.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/TextBlockFactory.java index 6a0268c..9247248 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/TextBlockFactory.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/factory/TextBlockFactory.java @@ -2,10 +2,10 @@ 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.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; +import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence; import lombok.AccessLevel; import lombok.experimental.FieldDefaults; diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/DocumentGraphMapper.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/DocumentGraphMapper.java index c51f9ec..13fa8e9 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/DocumentGraphMapper.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/DocumentGraphMapper.java @@ -7,11 +7,11 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; -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.internal.api.data.redaction.DocumentData; -import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructure; import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.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.Footer; diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java index 329bd40..f6c66cb 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/mapper/PropertiesMapper.java @@ -1,7 +1,6 @@ package com.knecon.fforesight.service.layoutparser.processor.services.mapper; import java.awt.geom.Rectangle2D; -import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -9,7 +8,6 @@ import java.util.Map; import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructure; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Image; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.ImageType; -import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Page; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Table; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.TableCell; diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFLinesTextStripper.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFLinesTextStripper.java index 09a8eb2..1a04cba 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFLinesTextStripper.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFLinesTextStripper.java @@ -329,6 +329,7 @@ public class PDFLinesTextStripper extends PDFTextStripper { .getXDirAdj() - (previous.getXDirAdj() + previous.getWidthDirAdj()) < maximumGapSize; } + @Override public String getText(PDDocument doc) throws IOException { diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFTextStripper.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFTextStripper.java index 1ca5b43..b3bed93 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFTextStripper.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/parsing/PDFTextStripper.java @@ -25,10 +25,23 @@ import java.io.StringWriter; import java.io.Writer; import java.text.Bidi; import java.text.Normalizer; -import java.util.*; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.StringTokenizer; +import java.util.TreeMap; +import java.util.TreeSet; import java.util.regex.Pattern; -import lombok.Getter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.pdfbox.cos.COSDictionary; @@ -46,6 +59,8 @@ import org.apache.pdfbox.text.TextPositionComparator; import com.knecon.fforesight.service.layoutparser.processor.utils.QuickSort; +import lombok.Getter; + /** * This is just a copy except i only adjusted lines 594-607 cause this is a bug in Pdfbox. * see S416.pdf @@ -194,40 +209,33 @@ public class PDFTextStripper extends LegacyPDFStreamEngine { } + public void beginMarkedContentSequence(COSName tag, COSDictionary properties) { - public void beginMarkedContentSequence(COSName tag, COSDictionary properties) - { PDMarkedContent markedContent = PDMarkedContent.create(tag, properties); - if (this.currentMarkedContents.isEmpty()) - { + if (this.currentMarkedContents.isEmpty()) { this.markedContents.add(markedContent); - } - else - { - PDMarkedContent currentMarkedContent = - this.currentMarkedContents.peek(); - if (currentMarkedContent != null) - { + } else { + PDMarkedContent currentMarkedContent = this.currentMarkedContents.peek(); + if (currentMarkedContent != null) { currentMarkedContent.addMarkedContent(markedContent); } } this.currentMarkedContents.push(markedContent); } + @Override - public void endMarkedContentSequence() - { - if (!this.currentMarkedContents.isEmpty()) - { + public void endMarkedContentSequence() { + + if (!this.currentMarkedContents.isEmpty()) { this.currentMarkedContents.pop(); } } - public void xobject(PDXObject xobject) - { - if (!this.currentMarkedContents.isEmpty()) - { + public void xobject(PDXObject xobject) { + + if (!this.currentMarkedContents.isEmpty()) { this.currentMarkedContents.peek().addXObject(xobject); } } @@ -635,7 +643,6 @@ public class PDFTextStripper extends LegacyPDFStreamEngine { var normalized = normalize(line); // normalized.stream().filter(l -> System.out.println(l.getText().contains("Plenarprotokoll 20/24")).findFirst().isPresent() - lastLineStartPosition = handleLineSeparation(current, lastPosition, lastLineStartPosition, maxHeightForLine); writeLine(normalized, current.isParagraphStart); line.clear(); @@ -914,8 +921,7 @@ public class PDFTextStripper extends LegacyPDFStreamEngine { textList.add(text); } } - if (!this.currentMarkedContents.isEmpty()) - { + if (!this.currentMarkedContents.isEmpty()) { this.currentMarkedContents.peek().addText(text); } } @@ -2102,7 +2108,9 @@ public class PDFTextStripper extends LegacyPDFStreamEngine { return endParagraphWritten; } - public void setEndParagraphWritten(){ + + public void setEndParagraphWritten() { + endParagraphWritten = true; } @@ -2145,7 +2153,6 @@ public class PDFTextStripper extends LegacyPDFStreamEngine { this.isHangingIndent = true; } - } } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/visualization/ViewerDocumentService.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/visualization/ViewerDocumentService.java index 2290eed..713cba3 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/visualization/ViewerDocumentService.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/services/visualization/ViewerDocumentService.java @@ -1,10 +1,13 @@ package com.knecon.fforesight.service.layoutparser.processor.services.visualization; +import java.awt.Color; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.OutputStream; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; import org.apache.pdfbox.cos.COSDictionary; @@ -30,6 +33,7 @@ import com.knecon.fforesight.service.layoutparser.processor.model.visualization. import com.knecon.fforesight.service.layoutparser.processor.model.visualization.LayoutGrid; import com.knecon.fforesight.service.layoutparser.processor.model.visualization.PlacedText; import com.knecon.fforesight.service.layoutparser.processor.model.visualization.VisualizationsOnPage; +import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableCells; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; @@ -40,7 +44,6 @@ import lombok.extern.slf4j.Slf4j; @RequiredArgsConstructor public class ViewerDocumentService { - private static final String LAYER_NAME = "Layout grid"; private static final int FONT_SIZE = 10; public static final float LINE_WIDTH = 1f; @@ -49,7 +52,11 @@ public class ViewerDocumentService { @SneakyThrows - public void createViewerDocument(PDDocument pdDocument, Document document, OutputStream outputStream, boolean layerVisibilityDefaultValue) { + public void createViewerDocument(PDDocument pdDocument, + Document document, + OutputStream outputStream, + Map> extractedTableCells, + boolean layerVisibilityDefaultValue) { LayoutGrid layoutGrid = layoutGridService.createLayoutGrid(document); // PDDocument.save() is very slow, since it actually traverses the entire pdf and writes a new one. @@ -98,6 +105,11 @@ public class ViewerDocumentService { contentStream.addRect((float) r.getX(), (float) r.getY(), (float) r.getWidth(), (float) r.getHeight()); contentStream.fill(); } + for (TableCells tableCells : extractedTableCells.get(pageNumber)) { + contentStream.setStrokingColor(Color.CYAN); + contentStream.addRect((float) tableCells.getX0(), (float) tableCells.getY0(), (float) tableCells.getWidth(), (float) tableCells.getHeight()); + contentStream.stroke(); + } for (PlacedText placedText : visualizationsOnPage.getPlacedTexts()) { contentStream.setFont(font, FONT_SIZE); contentStream.beginText(); diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/MarkedContentUtils.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/MarkedContentUtils.java index 799ac99..045a266 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/MarkedContentUtils.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/MarkedContentUtils.java @@ -1,12 +1,5 @@ package com.knecon.fforesight.service.layoutparser.processor.utils; -import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock; -import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence; -import lombok.experimental.UtilityClass; -import org.apache.pdfbox.cos.COSName; -import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDMarkedContent; -import org.apache.pdfbox.text.TextPosition; - import java.awt.geom.Rectangle2D; import java.util.Collection; import java.util.Collections; @@ -14,12 +7,22 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDMarkedContent; +import org.apache.pdfbox.text.TextPosition; + +import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock; +import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence; + +import lombok.experimental.UtilityClass; + @UtilityClass public class MarkedContentUtils { public static final String HEADER = "Header"; public static final String FOOTER = "Footer"; + public List getMarkedContentBboxPerLine(List markedContents, String subtype) { if (markedContents == null) { @@ -31,7 +34,8 @@ public class MarkedContentUtils { .filter(m -> m.getProperties() != null) .filter(m -> m.getProperties().getItem("Subtype") != null) .filter(m -> ((COSName) m.getProperties().getItem("Subtype")).getName().equals(subtype)) - .map(PDMarkedContent::getContents).flatMap(Collection::stream) + .map(PDMarkedContent::getContents) + .flatMap(Collection::stream) .filter(t -> t instanceof TextPosition) .map(t -> (TextPosition) t) .filter(t -> !t.getUnicode().equals(" ")) @@ -41,16 +45,19 @@ public class MarkedContentUtils { return Collections.emptyList(); } - return markedContentByYPosition.values().stream() - .map(textPositions -> new TextPositionSequence(textPositions.stream() - .toList(), 0, true) - .getRectangle()) - .map(t -> new Rectangle2D.Float(t.getTopLeft().getX(), t.getTopLeft().getY() - Math.abs(t.getHeight()), t.getWidth(), Math.abs(t.getHeight()))).collect(Collectors.toList()); + return markedContentByYPosition.values() + .stream() + .map(textPositions -> new TextPositionSequence(textPositions.stream().toList(), 0, true).getRectangle()) + .map(t -> new Rectangle2D.Float(t.getTopLeft().getX(), t.getTopLeft().getY() - Math.abs(t.getHeight()), t.getWidth(), Math.abs(t.getHeight()))) + .collect(Collectors.toList()); } public boolean intersects(TextPageBlock textBlock, Map> markedContentBboxPerType, String type) { - return markedContentBboxPerType.get(type) != null && markedContentBboxPerType.get(type).stream().anyMatch(rectangle -> rectangle.intersects(textBlock.getPdfMinX(), textBlock.getPdfMinY(), textBlock.getWidth(), textBlock.getHeight())); + + return markedContentBboxPerType.get(type) != null && markedContentBboxPerType.get(type) + .stream() + .anyMatch(rectangle -> rectangle.intersects(textBlock.getPdfMinX(), textBlock.getPdfMinY(), textBlock.getWidth(), textBlock.getHeight())); } } diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/PositionUtils.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/PositionUtils.java index 48b720d..3aecb92 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/PositionUtils.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/PositionUtils.java @@ -19,10 +19,9 @@ public final class PositionUtils { double threshold = textBlock.getMostPopularWordHeight() * 3; - if (textBlock.getPdfMinX() + threshold > btf.getTopLeft().getX() - && textBlock.getPdfMaxX() - threshold < btf.getTopLeft().getX() + btf.getWidth() - && textBlock.getPdfMinY() + threshold > btf.getTopLeft().getY() - && textBlock.getPdfMaxY() - threshold < btf.getTopLeft().getY() + btf.getHeight()) { + if (textBlock.getPdfMinX() + threshold > btf.getTopLeft().getX() && textBlock.getPdfMaxX() - threshold < btf.getTopLeft() + .getX() + btf.getWidth() && textBlock.getPdfMinY() + threshold > btf.getTopLeft().getY() && textBlock.getPdfMaxY() - threshold < btf.getTopLeft() + .getY() + btf.getHeight()) { return true; } else { return false; diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/RectangleTransformations.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/RectangleTransformations.java index 1a49607..f0ea28f 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/RectangleTransformations.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/RectangleTransformations.java @@ -41,11 +41,14 @@ public class RectangleTransformations { return atomicTextBlocks.stream().flatMap(atomicTextBlock -> atomicTextBlock.getPositions().stream()).collect(new Rectangle2DBBoxCollector()); } + + public static Collector collectBBox() { return new Rectangle2DBBoxCollector(); } + public static PDRectangle toPDRectangleBBox(List rectangles) { Rectangle2D rectangle2D = RectangleTransformations.rectangleBBox(rectangles); @@ -70,6 +73,7 @@ public class RectangleTransformations { return format("%f,%f,%f,%f", rectangle2D.getX(), rectangle2D.getY(), rectangle2D.getWidth(), rectangle2D.getHeight()); } + public static Rectangle2D rectangleBBox(List rectangles) { return rectangles.stream().map(RectangleTransformations::toRectangle2D).collect(new Rectangle2DBBoxCollector()); @@ -84,6 +88,7 @@ public class RectangleTransformations { -redactionLogRectangle.getHeight()); } + public static Rectangle2D toRectangle2D(PDRectangle rectangle) { return new Rectangle2D.Double(rectangle.getLowerLeftX(), rectangle.getLowerLeftY(), rectangle.getWidth(), rectangle.getHeight()); diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/TextPositionOperations.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/TextPositionOperations.java index 53e8c29..9927685 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/TextPositionOperations.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/TextPositionOperations.java @@ -3,7 +3,6 @@ package com.knecon.fforesight.service.layoutparser.processor.utils; import java.util.List; import java.util.stream.Collectors; - import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock; import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence; diff --git a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/TextPositionSequenceComparator.java b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/TextPositionSequenceComparator.java index 40dce07..1e122c6 100644 --- a/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/TextPositionSequenceComparator.java +++ b/layoutparser-service/layoutparser-service-processor/src/main/java/com/knecon/fforesight/service/layoutparser/processor/utils/TextPositionSequenceComparator.java @@ -28,15 +28,13 @@ import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPosit * * @author Ben Litchfield */ -public class TextPositionSequenceComparator implements Comparator -{ +public class TextPositionSequenceComparator implements Comparator { + @Override - public int compare(TextPositionSequence pos1, TextPositionSequence pos2) - { + public int compare(TextPositionSequence pos1, TextPositionSequence pos2) { // only compare text that is in the same direction int cmp1 = Float.compare(pos1.getDir().getDegrees(), pos2.getDir().getDegrees()); - if (cmp1 != 0) - { + if (cmp1 != 0) { return cmp1; } @@ -54,19 +52,13 @@ public class TextPositionSequenceComparator implements Comparator= pos1YTop && pos2YBottom <= pos1YBottom || - pos1YBottom >= pos2YTop && pos1YBottom <= pos2YBottom) - { + if (yDifference < .1 || pos2YBottom >= pos1YTop && pos2YBottom <= pos1YBottom || pos1YBottom >= pos2YTop && pos1YBottom <= pos2YBottom) { return Float.compare(x1, x2); - } - else if (pos1YBottom < pos2YBottom) - { + } else if (pos1YBottom < pos2YBottom) { return -1; - } - else - { + } else { return 1; } } + } diff --git a/layoutparser-service/layoutparser-service-server/src/main/java/com/knecon/fforesight/service/layoutparser/server/Application.java b/layoutparser-service/layoutparser-service-server/src/main/java/com/knecon/fforesight/service/layoutparser/server/Application.java index 75cfabd..5fdc714 100644 --- a/layoutparser-service/layoutparser-service-server/src/main/java/com/knecon/fforesight/service/layoutparser/server/Application.java +++ b/layoutparser-service/layoutparser-service-server/src/main/java/com/knecon/fforesight/service/layoutparser/server/Application.java @@ -14,7 +14,7 @@ import com.knecon.fforesight.service.layoutparser.server.queue.MessagingConfigur import com.knecon.fforesight.tenantcommons.MultiTenancyAutoConfiguration; @ImportAutoConfiguration({MultiTenancyAutoConfiguration.class}) -@Import({MetricsConfiguration.class, StorageAutoConfiguration.class, LayoutParsingServiceProcessorConfiguration.class, MessagingConfiguration.class}) +@Import({MetricsConfiguration.class, StorageAutoConfiguration.class, LayoutParsingServiceProcessorConfiguration.class, MessagingConfiguration.class}) @SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class}) public class Application { diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/DocumentDataTests.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/DocumentDataTests.java index 003b94b..f5e3061 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/DocumentDataTests.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/DocumentDataTests.java @@ -17,6 +17,7 @@ import com.knecon.fforesight.service.layoutparser.server.utils.BuildDocumentTest import lombok.SneakyThrows; public class DocumentDataTests extends BuildDocumentTest { + @Test @SneakyThrows public void createDocumentDataForAllFiles() { @@ -36,11 +37,12 @@ public class DocumentDataTests extends BuildDocumentTest { for (String pdfFileName : pdfFileNames) { System.out.println(pdfFileName); DocumentData documentData = DocumentDataMapper.toDocumentData(buildGraph(resource.getFile().toPath().getParent().relativize(Path.of(pdfFileName)).toString())); - File outputFile = Path.of(outPath).resolve(resource.getFile().toPath().relativize(Path.of(pdfFileName))).toFile(); + File outputFile = Path.of(outPath).resolve(resource.getFile().toPath().relativize(Path.of(pdfFileName))).toFile(); outputFile.toPath().getParent().toFile().mkdirs(); try (var out = new FileOutputStream(outputFile.toString().replace(".pdf", ".json"))) { ObjectMapperFactory.create().writeValue(out, documentData); } } } + } diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ExtractMarkedContentTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ExtractMarkedContentTest.java index e8ba602..e86e28a 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ExtractMarkedContentTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ExtractMarkedContentTest.java @@ -7,7 +7,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import lombok.SneakyThrows; import org.apache.pdfbox.Loader; import org.apache.pdfbox.cos.COSArray; import org.apache.pdfbox.cos.COSBase; @@ -25,18 +24,24 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.core.io.ClassPathResource; +import lombok.SneakyThrows; + /** * @author mkl */ @Disabled public class ExtractMarkedContentTest { + final static File RESULT_FOLDER = new File("target/test-outputs", "extract"); + @BeforeEach public void setUpBeforeClass() throws Exception { + RESULT_FOLDER.mkdirs(); } + /** * * How to replace a space with a word while extract the data from PDF using PDFBox @@ -52,6 +57,7 @@ public class ExtractMarkedContentTest { @Test @SneakyThrows public void testExtractTestWPhromma() throws IOException { + System.out.printf("\n\n===\n%s\n===\n", "testWPhromma.pdf"); try (PDDocument document = Loader.loadPDF(new ClassPathResource("files/bdr/Drucksache_19_9865.pdf").getFile())) { @@ -74,6 +80,7 @@ public class ExtractMarkedContentTest { } } + /** * * Get tag's related BBox's even though there is no attributes (/A in document catalog structure) related to Layout in PDFBox? @@ -88,9 +95,10 @@ public class ExtractMarkedContentTest { */ @Test public void testExtractResMultipage() throws IOException { + System.out.printf("\n\n===\n%s\n===\n", "res_multipage.pdf"); - try(PDDocument document = Loader.loadPDF(new ClassPathResource("files/bdr/Drucksache_19_9865.pdf").getFile())) { + try (PDDocument document = Loader.loadPDF(new ClassPathResource("files/bdr/Drucksache_19_9865.pdf").getFile())) { Map> markedContents = new HashMap<>(); @@ -111,6 +119,7 @@ public class ExtractMarkedContentTest { } } + /** * * PDFBOX-5613 - uncorrent paragraph split @@ -125,6 +134,7 @@ public class ExtractMarkedContentTest { */ @Test public void testExtractDailyReport() throws IOException { + System.out.printf("\n\n===\n%s\n===\n", "Daily Report.pdf"); try (PDDocument document = Loader.loadPDF(new ClassPathResource("files/bdr/Drucksache_19_9865.pdf").getFile())) { @@ -147,10 +157,12 @@ public class ExtractMarkedContentTest { } } + /** * @see #testExtractTestWPhromma() */ void showStructure(PDStructureNode node, Map> markedContents) { + String structType = null; PDPage page = null; if (node instanceof PDStructureElement) { @@ -166,7 +178,7 @@ public class ExtractMarkedContentTest { if (base instanceof COSDictionary) { showStructure(PDStructureNode.create((COSDictionary) base), markedContents); } else if (base instanceof COSNumber) { - showContent(((COSNumber)base).intValue(), theseMarkedContents); + showContent(((COSNumber) base).intValue(), theseMarkedContents); } else { System.out.printf("?%s\n", base); } @@ -174,7 +186,7 @@ public class ExtractMarkedContentTest { } else if (object instanceof PDStructureNode) { showStructure((PDStructureNode) object, markedContents); } else if (object instanceof Integer) { - showContent((Integer)object, theseMarkedContents); + showContent((Integer) object, theseMarkedContents); } else { System.out.printf("?%s\n", object); } @@ -183,21 +195,24 @@ public class ExtractMarkedContentTest { System.out.printf("\n", structType); } + /** * @see #showStructure(PDStructureNode, Map) * @see #testExtractTestWPhromma() */ void showContent(int mcid, Map theseMarkedContents) { + PDMarkedContent markedContent = theseMarkedContents != null ? theseMarkedContents.get(mcid) : null; List contents = markedContent != null ? markedContent.getContents() : Collections.emptyList(); - StringBuilder textContent = new StringBuilder(); + StringBuilder textContent = new StringBuilder(); for (Object object : contents) { if (object instanceof TextPosition) { - textContent.append(((TextPosition)object).getUnicode()); + textContent.append(((TextPosition) object).getUnicode()); } else { textContent.append("?" + object); } } System.out.printf("%s\n", textContent); } + } \ No newline at end of file diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ViewerDocumentTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ViewerDocumentTest.java index e8dd8d6..4587ed0 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ViewerDocumentTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/graph/ViewerDocumentTest.java @@ -2,31 +2,20 @@ package com.knecon.fforesight.service.layoutparser.server.graph; import java.io.FileOutputStream; import java.nio.file.Path; -import java.util.List; import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; -import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentData; -import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructure; -import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType; import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType; import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationDocument; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document; -import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Table; -import com.knecon.fforesight.service.layoutparser.processor.model.table.Cell; -import com.knecon.fforesight.service.layoutparser.processor.model.table.TablePageBlock; import com.knecon.fforesight.service.layoutparser.processor.python_api.model.image.ImageServiceResponse; import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableServiceResponse; import com.knecon.fforesight.service.layoutparser.processor.services.SectionsBuilderService; import com.knecon.fforesight.service.layoutparser.processor.services.classification.RedactManagerClassificationService; -import com.knecon.fforesight.service.layoutparser.processor.services.factory.DocumentGraphFactory; -import com.knecon.fforesight.service.layoutparser.processor.services.mapper.DocumentDataMapper; -import com.knecon.fforesight.service.layoutparser.processor.services.mapper.PropertiesMapper; import com.knecon.fforesight.service.layoutparser.processor.services.visualization.LayoutGridService; import com.knecon.fforesight.service.layoutparser.processor.services.visualization.ViewerDocumentService; import com.knecon.fforesight.service.layoutparser.server.utils.BuildDocumentTest; @@ -41,6 +30,7 @@ public class ViewerDocumentTest extends BuildDocumentTest { @Autowired private RedactManagerClassificationService redactManagerClassificationService; + @Test @SneakyThrows public void testViewerDocument() { @@ -55,6 +45,7 @@ public class ViewerDocumentTest extends BuildDocumentTest { } } + public ClassificationDocument buildClassificationDocument(PDDocument originDocument) { ClassificationDocument classificationDocument = layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER, diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/model/TextPositionSequenceTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/model/TextPositionSequenceTest.java index 0922e4b..9655260 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/model/TextPositionSequenceTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/model/TextPositionSequenceTest.java @@ -9,7 +9,6 @@ import org.apache.pdfbox.util.Matrix; import org.junit.jupiter.api.Test; import com.fasterxml.jackson.databind.ObjectMapper; -import com.iqser.red.storage.commons.properties.StorageProperties; import com.iqser.red.storage.commons.service.ObjectSerializer; import com.knecon.fforesight.service.layoutparser.processor.model.text.TextDirection; import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence; diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/GapAcrossLinesDetectionServiceTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/GapAcrossLinesDetectionServiceTest.java index db26f8a..0645313 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/GapAcrossLinesDetectionServiceTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/GapAcrossLinesDetectionServiceTest.java @@ -13,8 +13,8 @@ import com.knecon.fforesight.service.layoutparser.processor.model.PageInformatio import com.knecon.fforesight.service.layoutparser.processor.services.DividingColumnDetectionService; import com.knecon.fforesight.service.layoutparser.processor.services.GapDetectionService; import com.knecon.fforesight.service.layoutparser.processor.services.GapsAcrossLinesService; -import com.knecon.fforesight.service.layoutparser.processor.services.PageInformationService; import com.knecon.fforesight.service.layoutparser.processor.services.PageContentExtractor; +import com.knecon.fforesight.service.layoutparser.processor.services.PageInformationService; import com.knecon.fforesight.service.layoutparser.server.utils.visualizations.PdfDraw; import lombok.SneakyThrows; @@ -36,7 +36,8 @@ class GapAcrossLinesDetectionServiceTest { System.out.println("start column detection"); start = System.currentTimeMillis(); for (PageInformation pageInformation : pageInformations) { - GapInformation gapInformation = GapDetectionService.findGapsInLines(pageInformation.getPageContents().getSortedTextPositionSequences(), pageInformation.getMainBodyTextFrame()); + GapInformation gapInformation = GapDetectionService.findGapsInLines(pageInformation.getPageContents().getSortedTextPositionSequences(), + pageInformation.getMainBodyTextFrame()); columnsPerPage.add(GapsAcrossLinesService.detectXGapsAcrossLines(gapInformation, pageInformation.getMainBodyTextFrame())); } System.out.printf("Finished column detection in %d ms%n", System.currentTimeMillis() - start); diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/InvisibleTableDetectionServiceTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/InvisibleTableDetectionServiceTest.java index 7eb50e2..0185680 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/InvisibleTableDetectionServiceTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/InvisibleTableDetectionServiceTest.java @@ -12,8 +12,8 @@ import org.junit.jupiter.api.Test; import com.knecon.fforesight.service.layoutparser.processor.model.PageInformation; import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence; import com.knecon.fforesight.service.layoutparser.processor.services.InvisibleTableDetectionService; -import com.knecon.fforesight.service.layoutparser.processor.services.PageInformationService; import com.knecon.fforesight.service.layoutparser.processor.services.PageContentExtractor; +import com.knecon.fforesight.service.layoutparser.processor.services.PageInformationService; import com.knecon.fforesight.service.layoutparser.processor.utils.RectangleTransformations; import com.knecon.fforesight.service.layoutparser.server.utils.visualizations.PdfDraw; diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/MainBodyTextFrameExtractionServiceTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/MainBodyTextFrameExtractionServiceTest.java index 84c3ba2..4c07315 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/MainBodyTextFrameExtractionServiceTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/MainBodyTextFrameExtractionServiceTest.java @@ -22,7 +22,6 @@ class MainBodyTextFrameExtractionServiceTest { String tmpFileName = Path.of("/tmp/").resolve(Path.of(fileName).getFileName() + "_MAIN_BODY.pdf").toString(); List sortedTextPositionSequence = PageContentExtractor.getSortedPageContents(fileName); - } } \ No newline at end of file diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/PageContentExtractorTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/PageContentExtractorTest.java index cbaa195..2e81f89 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/PageContentExtractorTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/PageContentExtractorTest.java @@ -3,13 +3,12 @@ package com.knecon.fforesight.service.layoutparser.server.services; import java.nio.file.Path; import java.util.List; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; 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.utils.RectangleTransformations; import com.knecon.fforesight.service.layoutparser.processor.services.PageContentExtractor; +import com.knecon.fforesight.service.layoutparser.processor.utils.RectangleTransformations; import com.knecon.fforesight.service.layoutparser.server.utils.visualizations.PdfDraw; import lombok.SneakyThrows; @@ -27,14 +26,11 @@ class PageContentExtractorTest { PdfDraw.drawRectanglesPerPageNumberedByLine(fileName, textPositionPerPage.stream() - .map(t -> t.getSortedTextPositionSequences() - .stream() - .map(TextPositionSequence::getRectangle) - .map(RectangleTransformations::toRectangle2D) + .map(t -> t.getSortedTextPositionSequences().stream().map(TextPositionSequence::getRectangle).map(RectangleTransformations::toRectangle2D) //.map(textPositionSequence -> (Rectangle2D) new Rectangle2D.Double(textPositionSequence.getMaxXDirAdj(), textPositionSequence.getMaxYDirAdj(), textPositionSequence.getWidth(), textPositionSequence.getHeight())) - .map(List::of) - .toList()) - .toList(), tmpFileName); + .map(List::of).toList()) + .toList(), + tmpFileName); } } \ No newline at end of file diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/PageInformationServiceTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/PageInformationServiceTest.java index 15d8243..903bc4d 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/PageInformationServiceTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/services/PageInformationServiceTest.java @@ -7,8 +7,8 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import com.knecon.fforesight.service.layoutparser.processor.model.PageInformation; -import com.knecon.fforesight.service.layoutparser.processor.services.PageInformationService; import com.knecon.fforesight.service.layoutparser.processor.services.PageContentExtractor; +import com.knecon.fforesight.service.layoutparser.processor.services.PageInformationService; import com.knecon.fforesight.service.layoutparser.server.utils.visualizations.PdfDraw; import lombok.SneakyThrows; @@ -38,6 +38,7 @@ class PageInformationServiceTest { System.out.printf("Finished drawing rectangles in %d ms%n", System.currentTimeMillis() - start); } + @Test @Disabled @SneakyThrows diff --git a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/utils/AbstractTest.java b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/utils/AbstractTest.java index 4f56c76..b851cc4 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/utils/AbstractTest.java +++ b/layoutparser-service/layoutparser-service-server/src/test/java/com/knecon/fforesight/service/layoutparser/server/utils/AbstractTest.java @@ -1,5 +1,25 @@ package com.knecon.fforesight.service.layoutparser.server.utils; +import java.io.InputStream; +import java.util.Optional; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; +import org.springframework.core.io.ClassPathResource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + import com.iqser.red.commons.jackson.ObjectMapperFactory; import com.iqser.red.storage.commons.service.StorageService; import com.iqser.red.storage.commons.utils.FileSystemBackedStorageService; @@ -9,22 +29,8 @@ import com.knecon.fforesight.service.layoutparser.processor.LayoutParsingStorage import com.knecon.fforesight.service.layoutparser.server.Application; import com.knecon.fforesight.tenantcommons.TenantContext; import com.knecon.fforesight.tenantcommons.TenantsClient; -import lombok.SneakyThrows; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.*; -import org.springframework.core.io.ClassPathResource; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import java.io.InputStream; -import java.util.Optional; +import lombok.SneakyThrows; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -100,6 +106,7 @@ public abstract class AbstractTest { return buildDefaultLayoutParsingRequest(LayoutParsingType.REDACT_MANAGER); } + protected LayoutParsingRequest buildDefaultLayoutParsingRequest(LayoutParsingType layoutParsingType) { return LayoutParsingRequest.builder() @@ -116,6 +123,7 @@ public abstract class AbstractTest { .build(); } + @SneakyThrows protected LayoutParsingRequest prepareStorage(String file, String cvServiceResponseFile, String imageInfoFile) { @@ -152,7 +160,6 @@ public abstract class AbstractTest { @ComponentScan("com.knecon.fforesight.service.layoutparser") public static class TestConfiguration { - @Bean @Primary public StorageService inmemoryStorage() { diff --git a/layoutparser-service/layoutparser-service-server/src/test/resources/files/cv_tables/ScanRotationBorder.TABLES.json b/layoutparser-service/layoutparser-service-server/src/test/resources/files/cv_tables/ScanRotationBorder.TABLES.json index 9da357f..a01d1b8 100644 --- a/layoutparser-service/layoutparser-service-server/src/test/resources/files/cv_tables/ScanRotationBorder.TABLES.json +++ b/layoutparser-service/layoutparser-service-server/src/test/resources/files/cv_tables/ScanRotationBorder.TABLES.json @@ -1 +1,2020 @@ -{"dossierId": "9434219e-4274-4f8c-b0ae-ce3e4c9126be", "fileId": "8125ad623b76ee4189ac085b004e919e", "targetFileExtension": "ORIGIN.pdf.gz", "responseFileExtension": "TABLES.json.gz", "X-TENANT-ID": "documine", "operation": "table", "data": [{"pageInfo": {"number": 1, "rotation": 0, "width": 595.2000122070312, "height": 841.6799926757812}, "tableCells": [{"x0": 82.80000305175781, "y0": 594.0, "x1": 145.44000244140625, "y1": 616.3200073242188, "width": 62.63999938964844, "height": 22.32000732421875}, {"x0": 359.6400146484375, "y0": 597.239990234375, "x1": 401.3999938964844, "y1": 606.5999755859375, "width": 41.759979248046875, "height": 9.3599853515625}, {"x0": 401.760009765625, "y0": 594.3599853515625, "x1": 511.55999755859375, "y1": 606.5999755859375, "width": 109.79998779296875, "height": 12.239990234375}, {"x0": 0.36000001430511475, "y0": 0.3599853515625, "x1": 595.0800170898438, "y1": 593.6400146484375, "width": 594.7200170755386, "height": 593.280029296875}, {"x0": 401.760009765625, "y0": 583.199951171875, "x1": 456.1199951171875, "y1": 593.6400146484375, "width": 54.3599853515625, "height": 10.4400634765625}, {"x0": 456.8399963378906, "y0": 581.760009765625, "x1": 511.55999755859375, "y1": 593.6400146484375, "width": 54.720001220703125, "height": 11.8800048828125}, {"x0": 82.08000183105469, "y0": 552.239990234375, "x1": 145.0800018310547, "y1": 591.8399658203125, "width": 63.0, "height": 39.5999755859375}, {"x0": 145.8000030517578, "y0": 576.719970703125, "x1": 200.8800048828125, "y1": 590.0399780273438, "width": 55.08000183105469, "height": 13.32000732421875}, {"x0": 201.60000610351562, "y0": 575.6400146484375, "x1": 253.8000030517578, "y1": 588.5999755859375, "width": 52.19999694824219, "height": 12.9599609375}, {"x0": 254.52000427246094, "y0": 574.199951171875, "x1": 306.3599853515625, "y1": 587.1599731445312, "width": 51.83998107910156, "height": 12.96002197265625}, {"x0": 307.0799865722656, "y0": 572.760009765625, "x1": 358.9200134277344, "y1": 585.719970703125, "width": 51.84002685546875, "height": 12.9599609375}, {"x0": 359.6400146484375, "y0": 571.6799926757812, "x1": 401.0400085449219, "y1": 584.280029296875, "width": 41.399993896484375, "height": 12.60003662109375}, {"x0": 401.760009765625, "y0": 570.239990234375, "x1": 456.1199951171875, "y1": 583.199951171875, "width": 54.3599853515625, "height": 12.9599609375}, {"x0": 456.8399963378906, "y0": 568.7999877929688, "x1": 510.8399963378906, "y1": 581.760009765625, "width": 54.0, "height": 12.96002197265625}, {"x0": 145.8000030517578, "y0": 563.760009765625, "x1": 200.52000427246094, "y1": 576.719970703125, "width": 54.720001220703125, "height": 12.9599609375}, {"x0": 201.60000610351562, "y0": 562.6799926757812, "x1": 253.44000244140625, "y1": 575.280029296875, "width": 51.839996337890625, "height": 12.60003662109375}, {"x0": 254.52000427246094, "y0": 561.239990234375, "x1": 306.0, "y1": 573.8399658203125, "width": 51.47999572753906, "height": 12.5999755859375}, {"x0": 306.7200012207031, "y0": 559.7999877929688, "x1": 358.55999755859375, "y1": 572.760009765625, "width": 51.839996337890625, "height": 12.96002197265625}, {"x0": 359.2799987792969, "y0": 559.0799560546875, "x1": 400.67999267578125, "y1": 571.3200073242188, "width": 41.399993896484375, "height": 12.24005126953125}, {"x0": 401.760009765625, "y0": 557.6400146484375, "x1": 455.760009765625, "y1": 570.239990234375, "width": 54.0, "height": 12.5999755859375}, {"x0": 456.4800109863281, "y0": 556.199951171875, "x1": 510.8399963378906, "y1": 568.7999877929688, "width": 54.3599853515625, "height": 12.60003662109375}, {"x0": 145.44000244140625, "y0": 550.7999877929688, "x1": 200.52000427246094, "y1": 564.1199951171875, "width": 55.08000183105469, "height": 13.32000732421875}, {"x0": 201.24000549316406, "y0": 549.719970703125, "x1": 253.44000244140625, "y1": 562.6799926757812, "width": 52.19999694824219, "height": 12.96002197265625}, {"x0": 254.16000366210938, "y0": 548.280029296875, "x1": 305.6400146484375, "y1": 561.239990234375, "width": 51.480010986328125, "height": 12.9599609375}, {"x0": 306.7200012207031, "y0": 546.8399658203125, "x1": 358.20001220703125, "y1": 559.7999877929688, "width": 51.480010986328125, "height": 12.96002197265625}, {"x0": 359.2799987792969, "y0": 546.1199951171875, "x1": 400.67999267578125, "y1": 558.3599853515625, "width": 41.399993896484375, "height": 12.239990234375}, {"x0": 401.3999938964844, "y0": 544.6799926757812, "x1": 455.3999938964844, "y1": 557.6400146484375, "width": 54.0, "height": 12.96002197265625}, {"x0": 456.4800109863281, "y0": 543.239990234375, "x1": 510.4800109863281, "y1": 556.199951171875, "width": 54.0, "height": 12.9599609375}, {"x0": 81.72000122070312, "y0": 539.280029296875, "x1": 144.36000061035156, "y1": 552.5999755859375, "width": 62.63999938964844, "height": 13.3199462890625}, {"x0": 145.0800018310547, "y0": 537.8399658203125, "x1": 200.16000366210938, "y1": 550.7999877929688, "width": 55.08000183105469, "height": 12.96002197265625}, {"x0": 201.24000549316406, "y0": 536.760009765625, "x1": 253.0800018310547, "y1": 549.719970703125, "width": 51.839996337890625, "height": 12.9599609375}, {"x0": 253.8000030517578, "y0": 535.3200073242188, "x1": 305.6400146484375, "y1": 548.280029296875, "width": 51.84001159667969, "height": 12.96002197265625}, {"x0": 306.3599853515625, "y0": 534.239990234375, "x1": 358.20001220703125, "y1": 546.8399658203125, "width": 51.84002685546875, "height": 12.5999755859375}, {"x0": 358.9200134277344, "y0": 533.1600341796875, "x1": 400.32000732421875, "y1": 545.760009765625, "width": 41.399993896484375, "height": 12.5999755859375}, {"x0": 401.0400085449219, "y0": 531.719970703125, "x1": 455.3999938964844, "y1": 544.6799926757812, "width": 54.3599853515625, "height": 12.96002197265625}, {"x0": 456.1199951171875, "y0": 530.6400146484375, "x1": 510.1199951171875, "y1": 543.239990234375, "width": 54.0, "height": 12.5999755859375}, {"x0": 81.72000122070312, "y0": 526.3200073242188, "x1": 144.0, "y1": 539.6400146484375, "width": 62.279998779296875, "height": 13.32000732421875}, {"x0": 145.0800018310547, "y0": 524.8800048828125, "x1": 200.16000366210938, "y1": 538.199951171875, "width": 55.08000183105469, "height": 13.3199462890625}, {"x0": 200.8800048828125, "y0": 523.7999877929688, "x1": 252.72000122070312, "y1": 536.760009765625, "width": 51.839996337890625, "height": 12.96002197265625}, {"x0": 253.8000030517578, "y0": 522.3599853515625, "x1": 305.2799987792969, "y1": 535.3200073242188, "width": 51.47999572753906, "height": 12.96002197265625}, {"x0": 306.0, "y0": 521.280029296875, "x1": 357.8399963378906, "y1": 534.239990234375, "width": 51.839996337890625, "height": 12.9599609375}, {"x0": 358.55999755859375, "y0": 520.199951171875, "x1": 399.9599914550781, "y1": 533.1600341796875, "width": 41.399993896484375, "height": 12.9600830078125}, {"x0": 401.0400085449219, "y0": 519.1199951171875, "x1": 455.0400085449219, "y1": 531.719970703125, "width": 54.0, "height": 12.5999755859375}, {"x0": 455.760009765625, "y0": 518.0399780273438, "x1": 510.1199951171875, "y1": 530.6400146484375, "width": 54.3599853515625, "height": 12.60003662109375}, {"x0": 81.36000061035156, "y0": 513.0, "x1": 144.0, "y1": 526.6799926757812, "width": 62.63999938964844, "height": 13.67999267578125}, {"x0": 144.72000122070312, "y0": 511.91998291015625, "x1": 200.16000366210938, "y1": 524.8800048828125, "width": 55.44000244140625, "height": 12.96002197265625}, {"x0": 200.52000427246094, "y0": 510.8399963378906, "x1": 252.72000122070312, "y1": 523.7999877929688, "width": 52.19999694824219, "height": 12.959991455078125}, {"x0": 253.44000244140625, "y0": 509.7599792480469, "x1": 305.2799987792969, "y1": 522.3599853515625, "width": 51.839996337890625, "height": 12.600006103515625}, {"x0": 306.0, "y0": 508.67999267578125, "x1": 357.8399963378906, "y1": 520.9199829101562, "width": 51.839996337890625, "height": 12.239990234375}, {"x0": 358.55999755859375, "y0": 507.6000061035156, "x1": 399.9599914550781, "y1": 519.8399658203125, "width": 41.399993896484375, "height": 12.239959716796875}, {"x0": 400.67999267578125, "y0": 506.1600036621094, "x1": 455.0400085449219, "y1": 519.1199951171875, "width": 54.360015869140625, "height": 12.959991455078125}, {"x0": 455.760009765625, "y0": 505.0799865722656, "x1": 509.760009765625, "y1": 517.6799926757812, "width": 54.0, "height": 12.600006103515625}, {"x0": 81.0, "y0": 500.03997802734375, "x1": 143.63999938964844, "y1": 513.3599853515625, "width": 62.63999938964844, "height": 13.32000732421875}, {"x0": 144.36000061035156, "y0": 498.9599914550781, "x1": 200.16000366210938, "y1": 511.91998291015625, "width": 55.80000305175781, "height": 12.959991455078125}, {"x0": 200.52000427246094, "y0": 497.8800048828125, "x1": 252.72000122070312, "y1": 510.8399963378906, "width": 52.19999694824219, "height": 12.959991455078125}, {"x0": 253.0800018310547, "y0": 496.79998779296875, "x1": 305.2799987792969, "y1": 509.3999938964844, "width": 52.19999694824219, "height": 12.600006103515625}, {"x0": 305.6400146484375, "y0": 495.7200012207031, "x1": 357.8399963378906, "y1": 508.32000732421875, "width": 52.199981689453125, "height": 12.600006103515625}, {"x0": 358.20001220703125, "y0": 494.6399841308594, "x1": 399.9599914550781, "y1": 507.239990234375, "width": 41.759979248046875, "height": 12.600006103515625}, {"x0": 400.32000732421875, "y0": 493.55999755859375, "x1": 455.0400085449219, "y1": 506.1600036621094, "width": 54.720001220703125, "height": 12.600006103515625}, {"x0": 455.3999938964844, "y0": 492.1199951171875, "x1": 509.3999938964844, "y1": 505.0799865722656, "width": 54.0, "height": 12.959991455078125}, {"x0": 80.63999938964844, "y0": 474.1199951171875, "x1": 143.27999877929688, "y1": 500.3999938964844, "width": 62.63999938964844, "height": 26.279998779296875}, {"x0": 144.36000061035156, "y0": 486.0, "x1": 199.44000244140625, "y1": 498.9599914550781, "width": 55.08000183105469, "height": 12.959991455078125}, {"x0": 199.8000030517578, "y0": 484.91998291015625, "x1": 252.0, "y1": 497.8800048828125, "width": 52.19999694824219, "height": 12.96002197265625}, {"x0": 252.72000122070312, "y0": 483.8399963378906, "x1": 304.55999755859375, "y1": 496.44000244140625, "width": 51.839996337890625, "height": 12.600006103515625}, {"x0": 305.2799987792969, "y0": 482.7599792480469, "x1": 357.1199951171875, "y1": 495.3599853515625, "width": 51.839996337890625, "height": 12.600006103515625}, {"x0": 357.8399963378906, "y0": 482.03997802734375, "x1": 399.239990234375, "y1": 494.2799987792969, "width": 41.399993896484375, "height": 12.240020751953125}, {"x0": 399.9599914550781, "y0": 480.6000061035156, "x1": 454.32000732421875, "y1": 493.55999755859375, "width": 54.360015869140625, "height": 12.959991455078125}, {"x0": 454.67999267578125, "y0": 479.5199890136719, "x1": 509.3999938964844, "y1": 492.1199951171875, "width": 54.720001220703125, "height": 12.600006103515625}, {"x0": 144.36000061035156, "y0": 473.03997802734375, "x1": 199.0800018310547, "y1": 486.0, "width": 54.720001220703125, "height": 12.96002197265625}, {"x0": 199.8000030517578, "y0": 471.9599914550781, "x1": 252.0, "y1": 484.91998291015625, "width": 52.19999694824219, "height": 12.959991455078125}, {"x0": 252.72000122070312, "y0": 470.8800048828125, "x1": 304.20001220703125, "y1": 483.47998046875, "width": 51.480010986328125, "height": 12.5999755859375}, {"x0": 305.2799987792969, "y0": 469.79998779296875, "x1": 356.760009765625, "y1": 482.3999938964844, "width": 51.480010986328125, "height": 12.600006103515625}, {"x0": 357.8399963378906, "y0": 469.0799865722656, "x1": 398.8800048828125, "y1": 481.32000732421875, "width": 41.040008544921875, "height": 12.240020751953125}, {"x0": 399.9599914550781, "y0": 467.6399841308594, "x1": 453.9599914550781, "y1": 480.6000061035156, "width": 54.0, "height": 12.96002197265625}, {"x0": 454.67999267578125, "y0": 466.55999755859375, "x1": 509.0400085449219, "y1": 479.1600036621094, "width": 54.360015869140625, "height": 12.600006103515625}, {"x0": 80.27999877929688, "y0": 461.5199890136719, "x1": 142.9199981689453, "y1": 474.47998046875, "width": 62.63999938964844, "height": 12.959991455078125}, {"x0": 143.63999938964844, "y0": 460.44000244140625, "x1": 198.72000122070312, "y1": 473.03997802734375, "width": 55.08000183105469, "height": 12.5999755859375}, {"x0": 199.8000030517578, "y0": 459.0, "x1": 251.63999938964844, "y1": 471.9599914550781, "width": 51.839996337890625, "height": 12.959991455078125}, {"x0": 252.36000061035156, "y0": 458.2799987792969, "x1": 304.20001220703125, "y1": 470.5199890136719, "width": 51.84001159667969, "height": 12.239990234375}, {"x0": 304.9200134277344, "y0": 456.8399963378906, "x1": 356.760009765625, "y1": 469.79998779296875, "width": 51.839996337890625, "height": 12.959991455078125}, {"x0": 357.4800109863281, "y0": 456.1199951171875, "x1": 398.8800048828125, "y1": 468.3599853515625, "width": 41.399993896484375, "height": 12.239990234375}, {"x0": 399.6000061035156, "y0": 455.03997802734375, "x1": 453.6000061035156, "y1": 467.6399841308594, "width": 54.0, "height": 12.600006103515625}, {"x0": 454.67999267578125, "y0": 453.9599914550781, "x1": 508.67999267578125, "y1": 466.55999755859375, "width": 54.0, "height": 12.600006103515625}, {"x0": 80.27999877929688, "y0": 448.1999816894531, "x1": 142.55999755859375, "y1": 461.5199890136719, "width": 62.279998779296875, "height": 13.32000732421875}, {"x0": 143.63999938964844, "y0": 447.1199951171875, "x1": 198.72000122070312, "y1": 460.0799865722656, "width": 55.08000183105469, "height": 12.959991455078125}, {"x0": 199.44000244140625, "y0": 446.03997802734375, "x1": 251.27999877929688, "y1": 459.0, "width": 51.839996337890625, "height": 12.96002197265625}, {"x0": 252.36000061035156, "y0": 445.32000732421875, "x1": 303.8399963378906, "y1": 457.91998291015625, "width": 51.47999572753906, "height": 12.5999755859375}, {"x0": 304.55999755859375, "y0": 443.8800048828125, "x1": 356.3999938964844, "y1": 456.8399963378906, "width": 51.839996337890625, "height": 12.959991455078125}, {"x0": 357.1199951171875, "y0": 443.1600036621094, "x1": 398.5199890136719, "y1": 455.3999938964844, "width": 41.399993896484375, "height": 12.239990234375}, {"x0": 399.6000061035156, "y0": 442.0799865722656, "x1": 453.239990234375, "y1": 454.67999267578125, "width": 53.639984130859375, "height": 12.600006103515625}, {"x0": 454.32000732421875, "y0": 441.0, "x1": 508.67999267578125, "y1": 453.6000061035156, "width": 54.3599853515625, "height": 12.600006103515625}, {"x0": 79.91999816894531, "y0": 435.239990234375, "x1": 142.55999755859375, "y1": 448.55999755859375, "width": 62.63999938964844, "height": 13.32000732421875}, {"x0": 143.27999877929688, "y0": 434.5199890136719, "x1": 198.36000061035156, "y1": 447.1199951171875, "width": 55.08000183105469, "height": 12.600006103515625}, {"x0": 199.0800018310547, "y0": 433.44000244140625, "x1": 251.27999877929688, "y1": 446.03997802734375, "width": 52.19999694824219, "height": 12.5999755859375}, {"x0": 252.0, "y0": 432.3599853515625, "x1": 303.4800109863281, "y1": 444.6000061035156, "width": 51.480010986328125, "height": 12.240020751953125}, {"x0": 304.55999755859375, "y0": 431.2799987792969, "x1": 356.0400085449219, "y1": 443.8800048828125, "width": 51.480010986328125, "height": 12.600006103515625}, {"x0": 357.1199951171875, "y0": 430.55999755859375, "x1": 398.5199890136719, "y1": 442.79998779296875, "width": 41.399993896484375, "height": 12.239990234375}, {"x0": 399.239990234375, "y0": 429.47998046875, "x1": 453.239990234375, "y1": 441.7200012207031, "width": 54.0, "height": 12.240020751953125}, {"x0": 454.32000732421875, "y0": 428.3999938964844, "x1": 508.67999267578125, "y1": 441.0, "width": 54.3599853515625, "height": 12.600006103515625}, {"x0": 79.55999755859375, "y0": 409.32000732421875, "x1": 142.55999755859375, "y1": 435.6000061035156, "width": 63.0, "height": 26.279998779296875}, {"x0": 143.27999877929688, "y0": 421.1999816894531, "x1": 198.0, "y1": 434.1600036621094, "width": 54.720001220703125, "height": 12.96002197265625}, {"x0": 199.0800018310547, "y0": 420.47998046875, "x1": 250.9199981689453, "y1": 433.0799865722656, "width": 51.839996337890625, "height": 12.600006103515625}, {"x0": 251.63999938964844, "y0": 419.3999938964844, "x1": 303.1199951171875, "y1": 432.0, "width": 51.47999572753906, "height": 12.600006103515625}, {"x0": 304.20001220703125, "y0": 418.32000732421875, "x1": 356.0400085449219, "y1": 430.91998291015625, "width": 51.839996337890625, "height": 12.5999755859375}, {"x0": 356.760009765625, "y0": 417.6000061035156, "x1": 398.1600036621094, "y1": 430.1999816894531, "width": 41.399993896484375, "height": 12.5999755859375}, {"x0": 398.8800048828125, "y0": 416.5199890136719, "x1": 453.239990234375, "y1": 429.1199951171875, "width": 54.3599853515625, "height": 12.600006103515625}, {"x0": 453.9599914550781, "y0": 415.44000244140625, "x1": 508.32000732421875, "y1": 428.03997802734375, "width": 54.360015869140625, "height": 12.5999755859375}, {"x0": 142.9199981689453, "y0": 408.6000061035156, "x1": 198.0, "y1": 421.1999816894531, "width": 55.08000183105469, "height": 12.5999755859375}, {"x0": 198.72000122070312, "y0": 407.5199890136719, "x1": 250.9199981689453, "y1": 420.1199951171875, "width": 52.19999694824219, "height": 12.600006103515625}, {"x0": 251.63999938964844, "y0": 406.44000244140625, "x1": 303.1199951171875, "y1": 419.03997802734375, "width": 51.47999572753906, "height": 12.5999755859375}, {"x0": 303.8399963378906, "y0": 405.7200012207031, "x1": 356.0400085449219, "y1": 417.9599914550781, "width": 52.20001220703125, "height": 12.239990234375}, {"x0": 356.3999938964844, "y0": 404.6399841308594, "x1": 398.1600036621094, "y1": 417.239990234375, "width": 41.760009765625, "height": 12.600006103515625}, {"x0": 398.8800048828125, "y0": 403.55999755859375, "x1": 453.239990234375, "y1": 416.5199890136719, "width": 54.3599853515625, "height": 12.959991455078125}, {"x0": 453.6000061035156, "y0": 402.8399963378906, "x1": 507.9599914550781, "y1": 415.0799865722656, "width": 54.3599853515625, "height": 12.239990234375}, {"x0": 79.19999694824219, "y0": 396.3599853515625, "x1": 141.83999633789062, "y1": 409.67999267578125, "width": 62.63999938964844, "height": 13.32000732421875}, {"x0": 142.9199981689453, "y0": 395.6399841308594, "x1": 198.0, "y1": 408.239990234375, "width": 55.08000183105469, "height": 12.600006103515625}, {"x0": 198.36000061035156, "y0": 394.55999755859375, "x1": 250.9199981689453, "y1": 407.1600036621094, "width": 52.55999755859375, "height": 12.600006103515625}, {"x0": 251.27999877929688, "y0": 393.8399963378906, "x1": 303.1199951171875, "y1": 406.44000244140625, "width": 51.839996337890625, "height": 12.600006103515625}, {"x0": 303.8399963378906, "y0": 392.7599792480469, "x1": 356.0400085449219, "y1": 405.0, "width": 52.20001220703125, "height": 12.240020751953125}, {"x0": 356.3999938964844, "y0": 392.03997802734375, "x1": 398.1600036621094, "y1": 404.2799987792969, "width": 41.760009765625, "height": 12.240020751953125}, {"x0": 398.5199890136719, "y0": 390.9599914550781, "x1": 453.239990234375, "y1": 403.55999755859375, "width": 54.720001220703125, "height": 12.600006103515625}, {"x0": 453.6000061035156, "y0": 389.8800048828125, "x1": 507.9599914550781, "y1": 402.47998046875, "width": 54.3599853515625, "height": 12.5999755859375}, {"x0": 79.19999694824219, "y0": 383.3999938964844, "x1": 141.83999633789062, "y1": 396.7200012207031, "width": 62.63999938964844, "height": 13.32000732421875}, {"x0": 142.55999755859375, "y0": 382.67999267578125, "x1": 197.27999877929688, "y1": 395.2799987792969, "width": 54.720001220703125, "height": 12.600006103515625}, {"x0": 198.0, "y0": 381.6000061035156, "x1": 250.1999969482422, "y1": 394.1999816894531, "width": 52.19999694824219, "height": 12.5999755859375}, {"x0": 250.9199981689453, "y0": 380.5199890136719, "x1": 302.760009765625, "y1": 393.47998046875, "width": 51.84001159667969, "height": 12.959991455078125}, {"x0": 303.1199951171875, "y0": 379.79998779296875, "x1": 355.32000732421875, "y1": 392.3999938964844, "width": 52.20001220703125, "height": 12.600006103515625}, {"x0": 355.67999267578125, "y0": 379.0799865722656, "x1": 397.44000244140625, "y1": 391.32000732421875, "width": 41.760009765625, "height": 12.240020751953125}, {"x0": 398.1600036621094, "y0": 378.0, "x1": 452.5199890136719, "y1": 390.6000061035156, "width": 54.3599853515625, "height": 12.600006103515625}, {"x0": 452.8800048828125, "y0": 376.91998291015625, "x1": 507.6000061035156, "y1": 389.8800048828125, "width": 54.720001220703125, "height": 12.96002197265625}, {"x0": 78.4800033569336, "y0": 357.47998046875, "x1": 141.47999572753906, "y1": 383.3999938964844, "width": 62.99999237060547, "height": 25.920013427734375}, {"x0": 142.1999969482422, "y0": 369.7200012207031, "x1": 197.27999877929688, "y1": 382.32000732421875, "width": 55.08000183105469, "height": 12.600006103515625}, {"x0": 198.0, "y0": 368.6399841308594, "x1": 249.83999633789062, "y1": 381.239990234375, "width": 51.839996337890625, "height": 12.600006103515625}, {"x0": 250.9199981689453, "y0": 367.55999755859375, "x1": 302.3999938964844, "y1": 380.5199890136719, "width": 51.47999572753906, "height": 12.959991455078125}, {"x0": 303.1199951171875, "y0": 366.8399963378906, "x1": 354.9599914550781, "y1": 379.44000244140625, "width": 51.839996337890625, "height": 12.600006103515625}, {"x0": 355.67999267578125, "y0": 366.1199951171875, "x1": 397.0799865722656, "y1": 378.3599853515625, "width": 41.399993896484375, "height": 12.239990234375}, {"x0": 398.1600036621094, "y0": 365.03997802734375, "x1": 452.1600036621094, "y1": 377.6399841308594, "width": 54.0, "height": 12.600006103515625}, {"x0": 452.8800048828125, "y0": 363.9599914550781, "x1": 507.239990234375, "y1": 376.55999755859375, "width": 54.3599853515625, "height": 12.600006103515625}, {"x0": 142.1999969482422, "y0": 356.3999938964844, "x1": 196.9199981689453, "y1": 369.3599853515625, "width": 54.720001220703125, "height": 12.959991455078125}, {"x0": 197.63999938964844, "y0": 355.67999267578125, "x1": 249.83999633789062, "y1": 368.2799987792969, "width": 52.19999694824219, "height": 12.600006103515625}, {"x0": 250.55999755859375, "y0": 354.6000061035156, "x1": 302.0400085449219, "y1": 367.55999755859375, "width": 51.480010986328125, "height": 12.959991455078125}, {"x0": 303.1199951171875, "y0": 353.8800048828125, "x1": 354.9599914550781, "y1": 366.47998046875, "width": 51.839996337890625, "height": 12.5999755859375}, {"x0": 355.67999267578125, "y0": 353.1600036621094, "x1": 397.0799865722656, "y1": 365.3999938964844, "width": 41.399993896484375, "height": 12.239990234375}, {"x0": 398.1600036621094, "y0": 351.7200012207031, "x1": 451.79998779296875, "y1": 364.67999267578125, "width": 53.639984130859375, "height": 12.959991455078125}, {"x0": 452.8800048828125, "y0": 351.0, "x1": 507.239990234375, "y1": 363.9599914550781, "width": 54.3599853515625, "height": 12.959991455078125}, {"x0": 78.12000274658203, "y0": 344.5199890136719, "x1": 141.1199951171875, "y1": 357.47998046875, "width": 62.99999237060547, "height": 12.959991455078125}, {"x0": 141.83999633789062, "y0": 343.79998779296875, "x1": 196.55999755859375, "y1": 356.03997802734375, "width": 54.720001220703125, "height": 12.239990234375}, {"x0": 197.63999938964844, "y0": 342.7200012207031, "x1": 249.47999572753906, "y1": 355.32000732421875, "width": 51.839996337890625, "height": 12.600006103515625}, {"x0": 250.1999969482422, "y0": 341.6399841308594, "x1": 302.0400085449219, "y1": 354.239990234375, "width": 51.84001159667969, "height": 12.600006103515625}, {"x0": 302.760009765625, "y0": 340.91998291015625, "x1": 354.6000061035156, "y1": 353.1600036621094, "width": 51.839996337890625, "height": 12.240020751953125}, {"x0": 355.67999267578125, "y0": 340.1999816894531, "x1": 396.7200012207031, "y1": 352.44000244140625, "width": 41.040008544921875, "height": 12.240020751953125}, {"x0": 397.44000244140625, "y0": 339.47998046875, "x1": 451.79998779296875, "y1": 351.7200012207031, "width": 54.3599853515625, "height": 12.240020751953125}, {"x0": 452.5199890136719, "y0": 338.3999938964844, "x1": 506.8800048828125, "y1": 350.6399841308594, "width": 54.360015869140625, "height": 12.239990234375}, {"x0": 78.12000274658203, "y0": 331.55999755859375, "x1": 140.75999450683594, "y1": 344.5199890136719, "width": 62.639991760253906, "height": 12.959991455078125}, {"x0": 141.47999572753906, "y0": 330.8399963378906, "x1": 196.55999755859375, "y1": 343.44000244140625, "width": 55.08000183105469, "height": 12.600006103515625}, {"x0": 197.27999877929688, "y0": 329.7599792480469, "x1": 249.1199951171875, "y1": 342.3599853515625, "width": 51.839996337890625, "height": 12.600006103515625}, {"x0": 250.1999969482422, "y0": 329.03997802734375, "x1": 301.67999267578125, "y1": 341.6399841308594, "width": 51.47999572753906, "height": 12.600006103515625}, {"x0": 302.760009765625, "y0": 327.96002197265625, "x1": 354.239990234375, "y1": 340.55999755859375, "width": 51.47998046875, "height": 12.5999755859375}, {"x0": 355.32000732421875, "y0": 327.239990234375, "x1": 396.3599853515625, "y1": 339.8399963378906, "width": 41.03997802734375, "height": 12.600006103515625}, {"x0": 397.44000244140625, "y0": 326.52001953125, "x1": 451.44000244140625, "y1": 339.1199951171875, "width": 54.0, "height": 12.5999755859375}, {"x0": 452.1600036621094, "y0": 325.44000244140625, "x1": 506.5199890136719, "y1": 338.03997802734375, "width": 54.3599853515625, "height": 12.5999755859375}, {"x0": 77.4000015258789, "y0": 293.03997802734375, "x1": 140.39999389648438, "y1": 331.55999755859375, "width": 62.99999237060547, "height": 38.52001953125}, {"x0": 141.47999572753906, "y0": 317.8800048828125, "x1": 196.1999969482422, "y1": 330.47998046875, "width": 54.720001220703125, "height": 12.5999755859375}, {"x0": 196.9199981689453, "y0": 317.15997314453125, "x1": 249.1199951171875, "y1": 329.39996337890625, "width": 52.19999694824219, "height": 12.239990234375}, {"x0": 249.83999633789062, "y0": 316.08001708984375, "x1": 301.32000732421875, "y1": 328.67999267578125, "width": 51.480010986328125, "height": 12.5999755859375}, {"x0": 302.3999938964844, "y0": 315.3599853515625, "x1": 354.239990234375, "y1": 327.96002197265625, "width": 51.839996337890625, "height": 12.60003662109375}, {"x0": 354.9599914550781, "y0": 314.6400146484375, "x1": 396.3599853515625, "y1": 326.8800048828125, "width": 41.399993896484375, "height": 12.239990234375}, {"x0": 397.0799865722656, "y0": 313.55999755859375, "x1": 451.0799865722656, "y1": 326.15997314453125, "width": 54.0, "height": 12.5999755859375}, {"x0": 452.1600036621094, "y0": 312.8399658203125, "x1": 506.5199890136719, "y1": 325.08001708984375, "width": 54.3599853515625, "height": 12.24005126953125}, {"x0": 141.1199951171875, "y0": 304.91998291015625, "x1": 195.83999633789062, "y1": 317.8800048828125, "width": 54.720001220703125, "height": 12.96002197265625}, {"x0": 196.9199981689453, "y0": 304.20001220703125, "x1": 248.75999450683594, "y1": 316.79998779296875, "width": 51.839996337890625, "height": 12.5999755859375}, {"x0": 249.47999572753906, "y0": 303.47998046875, "x1": 301.32000732421875, "y1": 315.719970703125, "width": 51.84001159667969, "height": 12.239990234375}, {"x0": 302.3999938964844, "y0": 302.39996337890625, "x1": 353.8800048828125, "y1": 315.0, "width": 51.480010986328125, "height": 12.60003662109375}, {"x0": 354.9599914550781, "y0": 301.67999267578125, "x1": 396.3599853515625, "y1": 314.27996826171875, "width": 41.399993896484375, "height": 12.5999755859375}, {"x0": 397.0799865722656, "y0": 300.5999755859375, "x1": 451.0799865722656, "y1": 313.55999755859375, "width": 54.0, "height": 12.96002197265625}, {"x0": 451.79998779296875, "y0": 299.8800048828125, "x1": 506.1600036621094, "y1": 312.47998046875, "width": 54.360015869140625, "height": 12.5999755859375}, {"x0": 140.75999450683594, "y0": 291.96002197265625, "x1": 195.83999633789062, "y1": 304.91998291015625, "width": 55.08000183105469, "height": 12.9599609375}, {"x0": 196.55999755859375, "y0": 291.239990234375, "x1": 248.75999450683594, "y1": 304.20001220703125, "width": 52.19999694824219, "height": 12.96002197265625}, {"x0": 249.47999572753906, "y0": 290.52001953125, "x1": 301.32000732421875, "y1": 303.1199951171875, "width": 51.84001159667969, "height": 12.5999755859375}, {"x0": 302.0400085449219, "y0": 289.44000244140625, "x1": 353.8800048828125, "y1": 302.03997802734375, "width": 51.839996337890625, "height": 12.5999755859375}, {"x0": 354.6000061035156, "y0": 288.719970703125, "x1": 396.3599853515625, "y1": 301.32000732421875, "width": 41.759979248046875, "height": 12.60003662109375}, {"x0": 396.7200012207031, "y0": 287.6400146484375, "x1": 451.0799865722656, "y1": 300.5999755859375, "width": 54.3599853515625, "height": 12.9599609375}, {"x0": 451.44000244140625, "y0": 286.91998291015625, "x1": 505.79998779296875, "y1": 299.52001953125, "width": 54.3599853515625, "height": 12.60003662109375}, {"x0": 77.04000091552734, "y0": 267.47998046875, "x1": 139.67999267578125, "y1": 293.03997802734375, "width": 62.639991760253906, "height": 25.55999755859375}, {"x0": 140.75999450683594, "y0": 279.3599853515625, "x1": 195.83999633789062, "y1": 291.96002197265625, "width": 55.08000183105469, "height": 12.60003662109375}, {"x0": 195.83999633789062, "y0": 278.27996826171875, "x1": 248.75999450683594, "y1": 290.8800048828125, "width": 52.91999816894531, "height": 12.60003662109375}, {"x0": 249.1199951171875, "y0": 277.55999755859375, "x1": 301.32000732421875, "y1": 290.15997314453125, "width": 52.20001220703125, "height": 12.5999755859375}, {"x0": 301.67999267578125, "y0": 276.8399658203125, "x1": 353.8800048828125, "y1": 289.08001708984375, "width": 52.20001220703125, "height": 12.24005126953125}, {"x0": 354.239990234375, "y0": 276.1199951171875, "x1": 396.3599853515625, "y1": 288.3599853515625, "width": 42.1199951171875, "height": 12.239990234375}, {"x0": 396.7200012207031, "y0": 275.03997802734375, "x1": 451.0799865722656, "y1": 287.6400146484375, "width": 54.3599853515625, "height": 12.60003662109375}, {"x0": 451.44000244140625, "y0": 273.96002197265625, "x1": 505.79998779296875, "y1": 286.55999755859375, "width": 54.3599853515625, "height": 12.5999755859375}, {"x0": 140.39999389648438, "y0": 266.39996337890625, "x1": 195.1199951171875, "y1": 279.0, "width": 54.720001220703125, "height": 12.60003662109375}, {"x0": 195.83999633789062, "y0": 265.32000732421875, "x1": 248.39999389648438, "y1": 278.27996826171875, "width": 52.55999755859375, "height": 12.9599609375}, {"x0": 248.75999450683594, "y0": 264.5999755859375, "x1": 300.6000061035156, "y1": 277.20001220703125, "width": 51.84001159667969, "height": 12.60003662109375}, {"x0": 301.32000732421875, "y0": 263.8800048828125, "x1": 353.1600036621094, "y1": 276.1199951171875, "width": 51.839996337890625, "height": 12.239990234375}, {"x0": 353.8800048828125, "y0": 263.15997314453125, "x1": 395.6400146484375, "y1": 275.39996337890625, "width": 41.760009765625, "height": 12.239990234375}, {"x0": 396.0, "y0": 262.08001708984375, "x1": 450.3599853515625, "y1": 274.67999267578125, "width": 54.3599853515625, "height": 12.5999755859375}, {"x0": 450.7200012207031, "y0": 261.0, "x1": 505.44000244140625, "y1": 273.96002197265625, "width": 54.720001220703125, "height": 12.96002197265625}, {"x0": 76.68000030517578, "y0": 254.52001953125, "x1": 139.32000732421875, "y1": 267.47998046875, "width": 62.64000701904297, "height": 12.9599609375}, {"x0": 140.0399932861328, "y0": 253.44000244140625, "x1": 195.1199951171875, "y1": 266.03997802734375, "width": 55.08000183105469, "height": 12.5999755859375}, {"x0": 195.83999633789062, "y0": 252.3599853515625, "x1": 248.0399932861328, "y1": 265.32000732421875, "width": 52.19999694824219, "height": 12.96002197265625}, {"x0": 248.75999450683594, "y0": 251.6400146484375, "x1": 300.239990234375, "y1": 264.239990234375, "width": 51.47999572753906, "height": 12.5999755859375}, {"x0": 301.32000732421875, "y0": 250.91998291015625, "x1": 353.1600036621094, "y1": 263.52001953125, "width": 51.839996337890625, "height": 12.60003662109375}, {"x0": 353.8800048828125, "y0": 250.20001220703125, "x1": 395.2799987792969, "y1": 262.44000244140625, "width": 41.399993896484375, "height": 12.239990234375}, {"x0": 396.0, "y0": 249.1199951171875, "x1": 450.3599853515625, "y1": 261.719970703125, "width": 54.3599853515625, "height": 12.5999755859375}, {"x0": 450.7200012207031, "y0": 248.03997802734375, "x1": 505.0799865722656, "y1": 261.0, "width": 54.3599853515625, "height": 12.96002197265625}, {"x0": 76.68000030517578, "y0": 241.55999755859375, "x1": 138.9600067138672, "y1": 254.52001953125, "width": 62.280006408691406, "height": 12.96002197265625}, {"x0": 140.0399932861328, "y0": 240.8399658203125, "x1": 194.75999450683594, "y1": 253.08001708984375, "width": 54.720001220703125, "height": 12.24005126953125}, {"x0": 195.47999572753906, "y0": 239.760009765625, "x1": 247.67999267578125, "y1": 252.3599853515625, "width": 52.19999694824219, "height": 12.5999755859375}, {"x0": 248.39999389648438, "y0": 239.03997802734375, "x1": 300.239990234375, "y1": 251.27996826171875, "width": 51.839996337890625, "height": 12.239990234375}, {"x0": 300.9599914550781, "y0": 237.96002197265625, "x1": 352.79998779296875, "y1": 250.55999755859375, "width": 51.839996337890625, "height": 12.5999755859375}, {"x0": 353.5199890136719, "y0": 237.239990234375, "x1": 394.9200134277344, "y1": 249.47998046875, "width": 41.4000244140625, "height": 12.239990234375}, {"x0": 396.0, "y0": 236.52001953125, "x1": 450.0, "y1": 248.760009765625, "width": 54.0, "height": 12.239990234375}, {"x0": 450.7200012207031, "y0": 235.79998779296875, "x1": 505.0799865722656, "y1": 248.03997802734375, "width": 54.3599853515625, "height": 12.239990234375}, {"x0": 75.23999786376953, "y0": 178.20001220703125, "x1": 138.9600067138672, "y1": 241.55999755859375, "width": 63.720008850097656, "height": 63.3599853515625}, {"x0": 139.67999267578125, "y0": 227.8800048828125, "x1": 194.39999389648438, "y1": 240.47998046875, "width": 54.720001220703125, "height": 12.5999755859375}, {"x0": 195.47999572753906, "y0": 227.15997314453125, "x1": 247.67999267578125, "y1": 239.39996337890625, "width": 52.19999694824219, "height": 12.239990234375}, {"x0": 248.0399932861328, "y0": 226.08001708984375, "x1": 299.8800048828125, "y1": 238.67999267578125, "width": 51.84001159667969, "height": 12.5999755859375}, {"x0": 300.9599914550781, "y0": 225.3599853515625, "x1": 352.44000244140625, "y1": 237.96002197265625, "width": 51.480010986328125, "height": 12.60003662109375}, {"x0": 353.5199890136719, "y0": 224.6400146484375, "x1": 394.9200134277344, "y1": 236.8800048828125, "width": 41.4000244140625, "height": 12.239990234375}, {"x0": 395.6400146484375, "y0": 223.91998291015625, "x1": 449.6400146484375, "y1": 236.52001953125, "width": 54.0, "height": 12.60003662109375}, {"x0": 450.7200012207031, "y0": 222.8399658203125, "x1": 504.7200012207031, "y1": 235.44000244140625, "width": 54.0, "height": 12.60003662109375}, {"x0": 139.32000732421875, "y0": 215.27996826171875, "x1": 194.39999389648438, "y1": 227.52001953125, "width": 55.079986572265625, "height": 12.24005126953125}, {"x0": 195.1199951171875, "y0": 214.55999755859375, "x1": 247.32000732421875, "y1": 226.79998779296875, "width": 52.20001220703125, "height": 12.239990234375}, {"x0": 248.0399932861328, "y0": 213.47998046875, "x1": 299.5199890136719, "y1": 225.719970703125, "width": 51.47999572753906, "height": 12.239990234375}, {"x0": 300.6000061035156, "y0": 212.760009765625, "x1": 352.44000244140625, "y1": 225.0, "width": 51.839996337890625, "height": 12.239990234375}, {"x0": 353.1600036621094, "y0": 212.03997802734375, "x1": 394.55999755859375, "y1": 224.27996826171875, "width": 41.399993896484375, "height": 12.239990234375}, {"x0": 395.2799987792969, "y0": 211.32000732421875, "x1": 449.6400146484375, "y1": 223.55999755859375, "width": 54.360015869140625, "height": 12.239990234375}, {"x0": 450.3599853515625, "y0": 210.239990234375, "x1": 504.3599853515625, "y1": 222.8399658203125, "width": 54.0, "height": 12.5999755859375}, {"x0": 139.32000732421875, "y0": 202.67999267578125, "x1": 194.0399932861328, "y1": 214.91998291015625, "width": 54.71998596191406, "height": 12.239990234375}, {"x0": 194.75999450683594, "y0": 201.96002197265625, "x1": 246.9600067138672, "y1": 214.20001220703125, "width": 52.20001220703125, "height": 12.239990234375}, {"x0": 248.0399932861328, "y0": 200.8800048828125, "x1": 299.5199890136719, "y1": 213.47998046875, "width": 51.47999572753906, "height": 12.5999755859375}, {"x0": 300.239990234375, "y0": 200.15997314453125, "x1": 352.0799865722656, "y1": 212.39996337890625, "width": 51.839996337890625, "height": 12.239990234375}, {"x0": 352.79998779296875, "y0": 199.44000244140625, "x1": 394.20001220703125, "y1": 211.67999267578125, "width": 41.4000244140625, "height": 12.239990234375}, {"x0": 395.2799987792969, "y0": 198.3599853515625, "x1": 448.9200134277344, "y1": 210.96002197265625, "width": 53.6400146484375, "height": 12.60003662109375}, {"x0": 450.0, "y0": 197.6400146484375, "x1": 504.3599853515625, "y1": 209.8800048828125, "width": 54.3599853515625, "height": 12.239990234375}, {"x0": 138.9600067138672, "y0": 190.08001708984375, "x1": 194.0399932861328, "y1": 202.32000732421875, "width": 55.079986572265625, "height": 12.239990234375}, {"x0": 194.75999450683594, "y0": 189.0, "x1": 246.9600067138672, "y1": 201.239990234375, "width": 52.20001220703125, "height": 12.239990234375}, {"x0": 247.67999267578125, "y0": 188.27996826171875, "x1": 299.1600036621094, "y1": 200.52001953125, "width": 51.480010986328125, "height": 12.24005126953125}, {"x0": 300.239990234375, "y0": 187.55999755859375, "x1": 351.7200012207031, "y1": 199.79998779296875, "width": 51.480010986328125, "height": 12.239990234375}, {"x0": 352.79998779296875, "y0": 186.8399658203125, "x1": 394.20001220703125, "y1": 199.08001708984375, "width": 41.4000244140625, "height": 12.24005126953125}, {"x0": 394.9200134277344, "y0": 185.760009765625, "x1": 448.9200134277344, "y1": 198.3599853515625, "width": 54.0, "height": 12.5999755859375}, {"x0": 450.0, "y0": 185.03997802734375, "x1": 504.0, "y1": 197.27996826171875, "width": 54.0, "height": 12.239990234375}, {"x0": 138.60000610351562, "y0": 177.1199951171875, "x1": 194.0399932861328, "y1": 189.719970703125, "width": 55.43998718261719, "height": 12.5999755859375}, {"x0": 194.75999450683594, "y0": 176.39996337890625, "x1": 246.9600067138672, "y1": 189.0, "width": 52.20001220703125, "height": 12.60003662109375}, {"x0": 247.32000732421875, "y0": 175.32000732421875, "x1": 299.1600036621094, "y1": 187.91998291015625, "width": 51.839996337890625, "height": 12.5999755859375}, {"x0": 299.8800048828125, "y0": 174.5999755859375, "x1": 351.7200012207031, "y1": 187.20001220703125, "width": 51.839996337890625, "height": 12.60003662109375}, {"x0": 352.44000244140625, "y0": 173.8800048828125, "x1": 394.20001220703125, "y1": 186.1199951171875, "width": 41.760009765625, "height": 12.239990234375}, {"x0": 394.55999755859375, "y0": 173.15997314453125, "x1": 448.9200134277344, "y1": 185.39996337890625, "width": 54.360015869140625, "height": 12.239990234375}, {"x0": 449.6400146484375, "y0": 172.08001708984375, "x1": 504.0, "y1": 184.67999267578125, "width": 54.3599853515625, "height": 12.5999755859375}]}]} \ No newline at end of file +{ + "dossierId": "9434219e-4274-4f8c-b0ae-ce3e4c9126be", + "fileId": "8125ad623b76ee4189ac085b004e919e", + "targetFileExtension": "ORIGIN.pdf.gz", + "responseFileExtension": "TABLES.json.gz", + "X-TENANT-ID": "documine", + "operation": "table", + "data": [ + { + "pageInfo": { + "number": 1, + "rotation": 0, + "width": 595.2000122070312, + "height": 841.6799926757812 + }, + "tableCells": [ + { + "x0": 82.80000305175781, + "y0": 594.0, + "x1": 145.44000244140625, + "y1": 616.3200073242188, + "width": 62.63999938964844, + "height": 22.32000732421875 + }, + { + "x0": 359.6400146484375, + "y0": 597.239990234375, + "x1": 401.3999938964844, + "y1": 606.5999755859375, + "width": 41.759979248046875, + "height": 9.3599853515625 + }, + { + "x0": 401.760009765625, + "y0": 594.3599853515625, + "x1": 511.55999755859375, + "y1": 606.5999755859375, + "width": 109.79998779296875, + "height": 12.239990234375 + }, + { + "x0": 0.36000001430511475, + "y0": 0.3599853515625, + "x1": 595.0800170898438, + "y1": 593.6400146484375, + "width": 594.7200170755386, + "height": 593.280029296875 + }, + { + "x0": 401.760009765625, + "y0": 583.199951171875, + "x1": 456.1199951171875, + "y1": 593.6400146484375, + "width": 54.3599853515625, + "height": 10.4400634765625 + }, + { + "x0": 456.8399963378906, + "y0": 581.760009765625, + "x1": 511.55999755859375, + "y1": 593.6400146484375, + "width": 54.720001220703125, + "height": 11.8800048828125 + }, + { + "x0": 82.08000183105469, + "y0": 552.239990234375, + "x1": 145.0800018310547, + "y1": 591.8399658203125, + "width": 63.0, + "height": 39.5999755859375 + }, + { + "x0": 145.8000030517578, + "y0": 576.719970703125, + "x1": 200.8800048828125, + "y1": 590.0399780273438, + "width": 55.08000183105469, + "height": 13.32000732421875 + }, + { + "x0": 201.60000610351562, + "y0": 575.6400146484375, + "x1": 253.8000030517578, + "y1": 588.5999755859375, + "width": 52.19999694824219, + "height": 12.9599609375 + }, + { + "x0": 254.52000427246094, + "y0": 574.199951171875, + "x1": 306.3599853515625, + "y1": 587.1599731445312, + "width": 51.83998107910156, + "height": 12.96002197265625 + }, + { + "x0": 307.0799865722656, + "y0": 572.760009765625, + "x1": 358.9200134277344, + "y1": 585.719970703125, + "width": 51.84002685546875, + "height": 12.9599609375 + }, + { + "x0": 359.6400146484375, + "y0": 571.6799926757812, + "x1": 401.0400085449219, + "y1": 584.280029296875, + "width": 41.399993896484375, + "height": 12.60003662109375 + }, + { + "x0": 401.760009765625, + "y0": 570.239990234375, + "x1": 456.1199951171875, + "y1": 583.199951171875, + "width": 54.3599853515625, + "height": 12.9599609375 + }, + { + "x0": 456.8399963378906, + "y0": 568.7999877929688, + "x1": 510.8399963378906, + "y1": 581.760009765625, + "width": 54.0, + "height": 12.96002197265625 + }, + { + "x0": 145.8000030517578, + "y0": 563.760009765625, + "x1": 200.52000427246094, + "y1": 576.719970703125, + "width": 54.720001220703125, + "height": 12.9599609375 + }, + { + "x0": 201.60000610351562, + "y0": 562.6799926757812, + "x1": 253.44000244140625, + "y1": 575.280029296875, + "width": 51.839996337890625, + "height": 12.60003662109375 + }, + { + "x0": 254.52000427246094, + "y0": 561.239990234375, + "x1": 306.0, + "y1": 573.8399658203125, + "width": 51.47999572753906, + "height": 12.5999755859375 + }, + { + "x0": 306.7200012207031, + "y0": 559.7999877929688, + "x1": 358.55999755859375, + "y1": 572.760009765625, + "width": 51.839996337890625, + "height": 12.96002197265625 + }, + { + "x0": 359.2799987792969, + "y0": 559.0799560546875, + "x1": 400.67999267578125, + "y1": 571.3200073242188, + "width": 41.399993896484375, + "height": 12.24005126953125 + }, + { + "x0": 401.760009765625, + "y0": 557.6400146484375, + "x1": 455.760009765625, + "y1": 570.239990234375, + "width": 54.0, + "height": 12.5999755859375 + }, + { + "x0": 456.4800109863281, + "y0": 556.199951171875, + "x1": 510.8399963378906, + "y1": 568.7999877929688, + "width": 54.3599853515625, + "height": 12.60003662109375 + }, + { + "x0": 145.44000244140625, + "y0": 550.7999877929688, + "x1": 200.52000427246094, + "y1": 564.1199951171875, + "width": 55.08000183105469, + "height": 13.32000732421875 + }, + { + "x0": 201.24000549316406, + "y0": 549.719970703125, + "x1": 253.44000244140625, + "y1": 562.6799926757812, + "width": 52.19999694824219, + "height": 12.96002197265625 + }, + { + "x0": 254.16000366210938, + "y0": 548.280029296875, + "x1": 305.6400146484375, + "y1": 561.239990234375, + "width": 51.480010986328125, + "height": 12.9599609375 + }, + { + "x0": 306.7200012207031, + "y0": 546.8399658203125, + "x1": 358.20001220703125, + "y1": 559.7999877929688, + "width": 51.480010986328125, + "height": 12.96002197265625 + }, + { + "x0": 359.2799987792969, + "y0": 546.1199951171875, + "x1": 400.67999267578125, + "y1": 558.3599853515625, + "width": 41.399993896484375, + "height": 12.239990234375 + }, + { + "x0": 401.3999938964844, + "y0": 544.6799926757812, + "x1": 455.3999938964844, + "y1": 557.6400146484375, + "width": 54.0, + "height": 12.96002197265625 + }, + { + "x0": 456.4800109863281, + "y0": 543.239990234375, + "x1": 510.4800109863281, + "y1": 556.199951171875, + "width": 54.0, + "height": 12.9599609375 + }, + { + "x0": 81.72000122070312, + "y0": 539.280029296875, + "x1": 144.36000061035156, + "y1": 552.5999755859375, + "width": 62.63999938964844, + "height": 13.3199462890625 + }, + { + "x0": 145.0800018310547, + "y0": 537.8399658203125, + "x1": 200.16000366210938, + "y1": 550.7999877929688, + "width": 55.08000183105469, + "height": 12.96002197265625 + }, + { + "x0": 201.24000549316406, + "y0": 536.760009765625, + "x1": 253.0800018310547, + "y1": 549.719970703125, + "width": 51.839996337890625, + "height": 12.9599609375 + }, + { + "x0": 253.8000030517578, + "y0": 535.3200073242188, + "x1": 305.6400146484375, + "y1": 548.280029296875, + "width": 51.84001159667969, + "height": 12.96002197265625 + }, + { + "x0": 306.3599853515625, + "y0": 534.239990234375, + "x1": 358.20001220703125, + "y1": 546.8399658203125, + "width": 51.84002685546875, + "height": 12.5999755859375 + }, + { + "x0": 358.9200134277344, + "y0": 533.1600341796875, + "x1": 400.32000732421875, + "y1": 545.760009765625, + "width": 41.399993896484375, + "height": 12.5999755859375 + }, + { + "x0": 401.0400085449219, + "y0": 531.719970703125, + "x1": 455.3999938964844, + "y1": 544.6799926757812, + "width": 54.3599853515625, + "height": 12.96002197265625 + }, + { + "x0": 456.1199951171875, + "y0": 530.6400146484375, + "x1": 510.1199951171875, + "y1": 543.239990234375, + "width": 54.0, + "height": 12.5999755859375 + }, + { + "x0": 81.72000122070312, + "y0": 526.3200073242188, + "x1": 144.0, + "y1": 539.6400146484375, + "width": 62.279998779296875, + "height": 13.32000732421875 + }, + { + "x0": 145.0800018310547, + "y0": 524.8800048828125, + "x1": 200.16000366210938, + "y1": 538.199951171875, + "width": 55.08000183105469, + "height": 13.3199462890625 + }, + { + "x0": 200.8800048828125, + "y0": 523.7999877929688, + "x1": 252.72000122070312, + "y1": 536.760009765625, + "width": 51.839996337890625, + "height": 12.96002197265625 + }, + { + "x0": 253.8000030517578, + "y0": 522.3599853515625, + "x1": 305.2799987792969, + "y1": 535.3200073242188, + "width": 51.47999572753906, + "height": 12.96002197265625 + }, + { + "x0": 306.0, + "y0": 521.280029296875, + "x1": 357.8399963378906, + "y1": 534.239990234375, + "width": 51.839996337890625, + "height": 12.9599609375 + }, + { + "x0": 358.55999755859375, + "y0": 520.199951171875, + "x1": 399.9599914550781, + "y1": 533.1600341796875, + "width": 41.399993896484375, + "height": 12.9600830078125 + }, + { + "x0": 401.0400085449219, + "y0": 519.1199951171875, + "x1": 455.0400085449219, + "y1": 531.719970703125, + "width": 54.0, + "height": 12.5999755859375 + }, + { + "x0": 455.760009765625, + "y0": 518.0399780273438, + "x1": 510.1199951171875, + "y1": 530.6400146484375, + "width": 54.3599853515625, + "height": 12.60003662109375 + }, + { + "x0": 81.36000061035156, + "y0": 513.0, + "x1": 144.0, + "y1": 526.6799926757812, + "width": 62.63999938964844, + "height": 13.67999267578125 + }, + { + "x0": 144.72000122070312, + "y0": 511.91998291015625, + "x1": 200.16000366210938, + "y1": 524.8800048828125, + "width": 55.44000244140625, + "height": 12.96002197265625 + }, + { + "x0": 200.52000427246094, + "y0": 510.8399963378906, + "x1": 252.72000122070312, + "y1": 523.7999877929688, + "width": 52.19999694824219, + "height": 12.959991455078125 + }, + { + "x0": 253.44000244140625, + "y0": 509.7599792480469, + "x1": 305.2799987792969, + "y1": 522.3599853515625, + "width": 51.839996337890625, + "height": 12.600006103515625 + }, + { + "x0": 306.0, + "y0": 508.67999267578125, + "x1": 357.8399963378906, + "y1": 520.9199829101562, + "width": 51.839996337890625, + "height": 12.239990234375 + }, + { + "x0": 358.55999755859375, + "y0": 507.6000061035156, + "x1": 399.9599914550781, + "y1": 519.8399658203125, + "width": 41.399993896484375, + "height": 12.239959716796875 + }, + { + "x0": 400.67999267578125, + "y0": 506.1600036621094, + "x1": 455.0400085449219, + "y1": 519.1199951171875, + "width": 54.360015869140625, + "height": 12.959991455078125 + }, + { + "x0": 455.760009765625, + "y0": 505.0799865722656, + "x1": 509.760009765625, + "y1": 517.6799926757812, + "width": 54.0, + "height": 12.600006103515625 + }, + { + "x0": 81.0, + "y0": 500.03997802734375, + "x1": 143.63999938964844, + "y1": 513.3599853515625, + "width": 62.63999938964844, + "height": 13.32000732421875 + }, + { + "x0": 144.36000061035156, + "y0": 498.9599914550781, + "x1": 200.16000366210938, + "y1": 511.91998291015625, + "width": 55.80000305175781, + "height": 12.959991455078125 + }, + { + "x0": 200.52000427246094, + "y0": 497.8800048828125, + "x1": 252.72000122070312, + "y1": 510.8399963378906, + "width": 52.19999694824219, + "height": 12.959991455078125 + }, + { + "x0": 253.0800018310547, + "y0": 496.79998779296875, + "x1": 305.2799987792969, + "y1": 509.3999938964844, + "width": 52.19999694824219, + "height": 12.600006103515625 + }, + { + "x0": 305.6400146484375, + "y0": 495.7200012207031, + "x1": 357.8399963378906, + "y1": 508.32000732421875, + "width": 52.199981689453125, + "height": 12.600006103515625 + }, + { + "x0": 358.20001220703125, + "y0": 494.6399841308594, + "x1": 399.9599914550781, + "y1": 507.239990234375, + "width": 41.759979248046875, + "height": 12.600006103515625 + }, + { + "x0": 400.32000732421875, + "y0": 493.55999755859375, + "x1": 455.0400085449219, + "y1": 506.1600036621094, + "width": 54.720001220703125, + "height": 12.600006103515625 + }, + { + "x0": 455.3999938964844, + "y0": 492.1199951171875, + "x1": 509.3999938964844, + "y1": 505.0799865722656, + "width": 54.0, + "height": 12.959991455078125 + }, + { + "x0": 80.63999938964844, + "y0": 474.1199951171875, + "x1": 143.27999877929688, + "y1": 500.3999938964844, + "width": 62.63999938964844, + "height": 26.279998779296875 + }, + { + "x0": 144.36000061035156, + "y0": 486.0, + "x1": 199.44000244140625, + "y1": 498.9599914550781, + "width": 55.08000183105469, + "height": 12.959991455078125 + }, + { + "x0": 199.8000030517578, + "y0": 484.91998291015625, + "x1": 252.0, + "y1": 497.8800048828125, + "width": 52.19999694824219, + "height": 12.96002197265625 + }, + { + "x0": 252.72000122070312, + "y0": 483.8399963378906, + "x1": 304.55999755859375, + "y1": 496.44000244140625, + "width": 51.839996337890625, + "height": 12.600006103515625 + }, + { + "x0": 305.2799987792969, + "y0": 482.7599792480469, + "x1": 357.1199951171875, + "y1": 495.3599853515625, + "width": 51.839996337890625, + "height": 12.600006103515625 + }, + { + "x0": 357.8399963378906, + "y0": 482.03997802734375, + "x1": 399.239990234375, + "y1": 494.2799987792969, + "width": 41.399993896484375, + "height": 12.240020751953125 + }, + { + "x0": 399.9599914550781, + "y0": 480.6000061035156, + "x1": 454.32000732421875, + "y1": 493.55999755859375, + "width": 54.360015869140625, + "height": 12.959991455078125 + }, + { + "x0": 454.67999267578125, + "y0": 479.5199890136719, + "x1": 509.3999938964844, + "y1": 492.1199951171875, + "width": 54.720001220703125, + "height": 12.600006103515625 + }, + { + "x0": 144.36000061035156, + "y0": 473.03997802734375, + "x1": 199.0800018310547, + "y1": 486.0, + "width": 54.720001220703125, + "height": 12.96002197265625 + }, + { + "x0": 199.8000030517578, + "y0": 471.9599914550781, + "x1": 252.0, + "y1": 484.91998291015625, + "width": 52.19999694824219, + "height": 12.959991455078125 + }, + { + "x0": 252.72000122070312, + "y0": 470.8800048828125, + "x1": 304.20001220703125, + "y1": 483.47998046875, + "width": 51.480010986328125, + "height": 12.5999755859375 + }, + { + "x0": 305.2799987792969, + "y0": 469.79998779296875, + "x1": 356.760009765625, + "y1": 482.3999938964844, + "width": 51.480010986328125, + "height": 12.600006103515625 + }, + { + "x0": 357.8399963378906, + "y0": 469.0799865722656, + "x1": 398.8800048828125, + "y1": 481.32000732421875, + "width": 41.040008544921875, + "height": 12.240020751953125 + }, + { + "x0": 399.9599914550781, + "y0": 467.6399841308594, + "x1": 453.9599914550781, + "y1": 480.6000061035156, + "width": 54.0, + "height": 12.96002197265625 + }, + { + "x0": 454.67999267578125, + "y0": 466.55999755859375, + "x1": 509.0400085449219, + "y1": 479.1600036621094, + "width": 54.360015869140625, + "height": 12.600006103515625 + }, + { + "x0": 80.27999877929688, + "y0": 461.5199890136719, + "x1": 142.9199981689453, + "y1": 474.47998046875, + "width": 62.63999938964844, + "height": 12.959991455078125 + }, + { + "x0": 143.63999938964844, + "y0": 460.44000244140625, + "x1": 198.72000122070312, + "y1": 473.03997802734375, + "width": 55.08000183105469, + "height": 12.5999755859375 + }, + { + "x0": 199.8000030517578, + "y0": 459.0, + "x1": 251.63999938964844, + "y1": 471.9599914550781, + "width": 51.839996337890625, + "height": 12.959991455078125 + }, + { + "x0": 252.36000061035156, + "y0": 458.2799987792969, + "x1": 304.20001220703125, + "y1": 470.5199890136719, + "width": 51.84001159667969, + "height": 12.239990234375 + }, + { + "x0": 304.9200134277344, + "y0": 456.8399963378906, + "x1": 356.760009765625, + "y1": 469.79998779296875, + "width": 51.839996337890625, + "height": 12.959991455078125 + }, + { + "x0": 357.4800109863281, + "y0": 456.1199951171875, + "x1": 398.8800048828125, + "y1": 468.3599853515625, + "width": 41.399993896484375, + "height": 12.239990234375 + }, + { + "x0": 399.6000061035156, + "y0": 455.03997802734375, + "x1": 453.6000061035156, + "y1": 467.6399841308594, + "width": 54.0, + "height": 12.600006103515625 + }, + { + "x0": 454.67999267578125, + "y0": 453.9599914550781, + "x1": 508.67999267578125, + "y1": 466.55999755859375, + "width": 54.0, + "height": 12.600006103515625 + }, + { + "x0": 80.27999877929688, + "y0": 448.1999816894531, + "x1": 142.55999755859375, + "y1": 461.5199890136719, + "width": 62.279998779296875, + "height": 13.32000732421875 + }, + { + "x0": 143.63999938964844, + "y0": 447.1199951171875, + "x1": 198.72000122070312, + "y1": 460.0799865722656, + "width": 55.08000183105469, + "height": 12.959991455078125 + }, + { + "x0": 199.44000244140625, + "y0": 446.03997802734375, + "x1": 251.27999877929688, + "y1": 459.0, + "width": 51.839996337890625, + "height": 12.96002197265625 + }, + { + "x0": 252.36000061035156, + "y0": 445.32000732421875, + "x1": 303.8399963378906, + "y1": 457.91998291015625, + "width": 51.47999572753906, + "height": 12.5999755859375 + }, + { + "x0": 304.55999755859375, + "y0": 443.8800048828125, + "x1": 356.3999938964844, + "y1": 456.8399963378906, + "width": 51.839996337890625, + "height": 12.959991455078125 + }, + { + "x0": 357.1199951171875, + "y0": 443.1600036621094, + "x1": 398.5199890136719, + "y1": 455.3999938964844, + "width": 41.399993896484375, + "height": 12.239990234375 + }, + { + "x0": 399.6000061035156, + "y0": 442.0799865722656, + "x1": 453.239990234375, + "y1": 454.67999267578125, + "width": 53.639984130859375, + "height": 12.600006103515625 + }, + { + "x0": 454.32000732421875, + "y0": 441.0, + "x1": 508.67999267578125, + "y1": 453.6000061035156, + "width": 54.3599853515625, + "height": 12.600006103515625 + }, + { + "x0": 79.91999816894531, + "y0": 435.239990234375, + "x1": 142.55999755859375, + "y1": 448.55999755859375, + "width": 62.63999938964844, + "height": 13.32000732421875 + }, + { + "x0": 143.27999877929688, + "y0": 434.5199890136719, + "x1": 198.36000061035156, + "y1": 447.1199951171875, + "width": 55.08000183105469, + "height": 12.600006103515625 + }, + { + "x0": 199.0800018310547, + "y0": 433.44000244140625, + "x1": 251.27999877929688, + "y1": 446.03997802734375, + "width": 52.19999694824219, + "height": 12.5999755859375 + }, + { + "x0": 252.0, + "y0": 432.3599853515625, + "x1": 303.4800109863281, + "y1": 444.6000061035156, + "width": 51.480010986328125, + "height": 12.240020751953125 + }, + { + "x0": 304.55999755859375, + "y0": 431.2799987792969, + "x1": 356.0400085449219, + "y1": 443.8800048828125, + "width": 51.480010986328125, + "height": 12.600006103515625 + }, + { + "x0": 357.1199951171875, + "y0": 430.55999755859375, + "x1": 398.5199890136719, + "y1": 442.79998779296875, + "width": 41.399993896484375, + "height": 12.239990234375 + }, + { + "x0": 399.239990234375, + "y0": 429.47998046875, + "x1": 453.239990234375, + "y1": 441.7200012207031, + "width": 54.0, + "height": 12.240020751953125 + }, + { + "x0": 454.32000732421875, + "y0": 428.3999938964844, + "x1": 508.67999267578125, + "y1": 441.0, + "width": 54.3599853515625, + "height": 12.600006103515625 + }, + { + "x0": 79.55999755859375, + "y0": 409.32000732421875, + "x1": 142.55999755859375, + "y1": 435.6000061035156, + "width": 63.0, + "height": 26.279998779296875 + }, + { + "x0": 143.27999877929688, + "y0": 421.1999816894531, + "x1": 198.0, + "y1": 434.1600036621094, + "width": 54.720001220703125, + "height": 12.96002197265625 + }, + { + "x0": 199.0800018310547, + "y0": 420.47998046875, + "x1": 250.9199981689453, + "y1": 433.0799865722656, + "width": 51.839996337890625, + "height": 12.600006103515625 + }, + { + "x0": 251.63999938964844, + "y0": 419.3999938964844, + "x1": 303.1199951171875, + "y1": 432.0, + "width": 51.47999572753906, + "height": 12.600006103515625 + }, + { + "x0": 304.20001220703125, + "y0": 418.32000732421875, + "x1": 356.0400085449219, + "y1": 430.91998291015625, + "width": 51.839996337890625, + "height": 12.5999755859375 + }, + { + "x0": 356.760009765625, + "y0": 417.6000061035156, + "x1": 398.1600036621094, + "y1": 430.1999816894531, + "width": 41.399993896484375, + "height": 12.5999755859375 + }, + { + "x0": 398.8800048828125, + "y0": 416.5199890136719, + "x1": 453.239990234375, + "y1": 429.1199951171875, + "width": 54.3599853515625, + "height": 12.600006103515625 + }, + { + "x0": 453.9599914550781, + "y0": 415.44000244140625, + "x1": 508.32000732421875, + "y1": 428.03997802734375, + "width": 54.360015869140625, + "height": 12.5999755859375 + }, + { + "x0": 142.9199981689453, + "y0": 408.6000061035156, + "x1": 198.0, + "y1": 421.1999816894531, + "width": 55.08000183105469, + "height": 12.5999755859375 + }, + { + "x0": 198.72000122070312, + "y0": 407.5199890136719, + "x1": 250.9199981689453, + "y1": 420.1199951171875, + "width": 52.19999694824219, + "height": 12.600006103515625 + }, + { + "x0": 251.63999938964844, + "y0": 406.44000244140625, + "x1": 303.1199951171875, + "y1": 419.03997802734375, + "width": 51.47999572753906, + "height": 12.5999755859375 + }, + { + "x0": 303.8399963378906, + "y0": 405.7200012207031, + "x1": 356.0400085449219, + "y1": 417.9599914550781, + "width": 52.20001220703125, + "height": 12.239990234375 + }, + { + "x0": 356.3999938964844, + "y0": 404.6399841308594, + "x1": 398.1600036621094, + "y1": 417.239990234375, + "width": 41.760009765625, + "height": 12.600006103515625 + }, + { + "x0": 398.8800048828125, + "y0": 403.55999755859375, + "x1": 453.239990234375, + "y1": 416.5199890136719, + "width": 54.3599853515625, + "height": 12.959991455078125 + }, + { + "x0": 453.6000061035156, + "y0": 402.8399963378906, + "x1": 507.9599914550781, + "y1": 415.0799865722656, + "width": 54.3599853515625, + "height": 12.239990234375 + }, + { + "x0": 79.19999694824219, + "y0": 396.3599853515625, + "x1": 141.83999633789062, + "y1": 409.67999267578125, + "width": 62.63999938964844, + "height": 13.32000732421875 + }, + { + "x0": 142.9199981689453, + "y0": 395.6399841308594, + "x1": 198.0, + "y1": 408.239990234375, + "width": 55.08000183105469, + "height": 12.600006103515625 + }, + { + "x0": 198.36000061035156, + "y0": 394.55999755859375, + "x1": 250.9199981689453, + "y1": 407.1600036621094, + "width": 52.55999755859375, + "height": 12.600006103515625 + }, + { + "x0": 251.27999877929688, + "y0": 393.8399963378906, + "x1": 303.1199951171875, + "y1": 406.44000244140625, + "width": 51.839996337890625, + "height": 12.600006103515625 + }, + { + "x0": 303.8399963378906, + "y0": 392.7599792480469, + "x1": 356.0400085449219, + "y1": 405.0, + "width": 52.20001220703125, + "height": 12.240020751953125 + }, + { + "x0": 356.3999938964844, + "y0": 392.03997802734375, + "x1": 398.1600036621094, + "y1": 404.2799987792969, + "width": 41.760009765625, + "height": 12.240020751953125 + }, + { + "x0": 398.5199890136719, + "y0": 390.9599914550781, + "x1": 453.239990234375, + "y1": 403.55999755859375, + "width": 54.720001220703125, + "height": 12.600006103515625 + }, + { + "x0": 453.6000061035156, + "y0": 389.8800048828125, + "x1": 507.9599914550781, + "y1": 402.47998046875, + "width": 54.3599853515625, + "height": 12.5999755859375 + }, + { + "x0": 79.19999694824219, + "y0": 383.3999938964844, + "x1": 141.83999633789062, + "y1": 396.7200012207031, + "width": 62.63999938964844, + "height": 13.32000732421875 + }, + { + "x0": 142.55999755859375, + "y0": 382.67999267578125, + "x1": 197.27999877929688, + "y1": 395.2799987792969, + "width": 54.720001220703125, + "height": 12.600006103515625 + }, + { + "x0": 198.0, + "y0": 381.6000061035156, + "x1": 250.1999969482422, + "y1": 394.1999816894531, + "width": 52.19999694824219, + "height": 12.5999755859375 + }, + { + "x0": 250.9199981689453, + "y0": 380.5199890136719, + "x1": 302.760009765625, + "y1": 393.47998046875, + "width": 51.84001159667969, + "height": 12.959991455078125 + }, + { + "x0": 303.1199951171875, + "y0": 379.79998779296875, + "x1": 355.32000732421875, + "y1": 392.3999938964844, + "width": 52.20001220703125, + "height": 12.600006103515625 + }, + { + "x0": 355.67999267578125, + "y0": 379.0799865722656, + "x1": 397.44000244140625, + "y1": 391.32000732421875, + "width": 41.760009765625, + "height": 12.240020751953125 + }, + { + "x0": 398.1600036621094, + "y0": 378.0, + "x1": 452.5199890136719, + "y1": 390.6000061035156, + "width": 54.3599853515625, + "height": 12.600006103515625 + }, + { + "x0": 452.8800048828125, + "y0": 376.91998291015625, + "x1": 507.6000061035156, + "y1": 389.8800048828125, + "width": 54.720001220703125, + "height": 12.96002197265625 + }, + { + "x0": 78.4800033569336, + "y0": 357.47998046875, + "x1": 141.47999572753906, + "y1": 383.3999938964844, + "width": 62.99999237060547, + "height": 25.920013427734375 + }, + { + "x0": 142.1999969482422, + "y0": 369.7200012207031, + "x1": 197.27999877929688, + "y1": 382.32000732421875, + "width": 55.08000183105469, + "height": 12.600006103515625 + }, + { + "x0": 198.0, + "y0": 368.6399841308594, + "x1": 249.83999633789062, + "y1": 381.239990234375, + "width": 51.839996337890625, + "height": 12.600006103515625 + }, + { + "x0": 250.9199981689453, + "y0": 367.55999755859375, + "x1": 302.3999938964844, + "y1": 380.5199890136719, + "width": 51.47999572753906, + "height": 12.959991455078125 + }, + { + "x0": 303.1199951171875, + "y0": 366.8399963378906, + "x1": 354.9599914550781, + "y1": 379.44000244140625, + "width": 51.839996337890625, + "height": 12.600006103515625 + }, + { + "x0": 355.67999267578125, + "y0": 366.1199951171875, + "x1": 397.0799865722656, + "y1": 378.3599853515625, + "width": 41.399993896484375, + "height": 12.239990234375 + }, + { + "x0": 398.1600036621094, + "y0": 365.03997802734375, + "x1": 452.1600036621094, + "y1": 377.6399841308594, + "width": 54.0, + "height": 12.600006103515625 + }, + { + "x0": 452.8800048828125, + "y0": 363.9599914550781, + "x1": 507.239990234375, + "y1": 376.55999755859375, + "width": 54.3599853515625, + "height": 12.600006103515625 + }, + { + "x0": 142.1999969482422, + "y0": 356.3999938964844, + "x1": 196.9199981689453, + "y1": 369.3599853515625, + "width": 54.720001220703125, + "height": 12.959991455078125 + }, + { + "x0": 197.63999938964844, + "y0": 355.67999267578125, + "x1": 249.83999633789062, + "y1": 368.2799987792969, + "width": 52.19999694824219, + "height": 12.600006103515625 + }, + { + "x0": 250.55999755859375, + "y0": 354.6000061035156, + "x1": 302.0400085449219, + "y1": 367.55999755859375, + "width": 51.480010986328125, + "height": 12.959991455078125 + }, + { + "x0": 303.1199951171875, + "y0": 353.8800048828125, + "x1": 354.9599914550781, + "y1": 366.47998046875, + "width": 51.839996337890625, + "height": 12.5999755859375 + }, + { + "x0": 355.67999267578125, + "y0": 353.1600036621094, + "x1": 397.0799865722656, + "y1": 365.3999938964844, + "width": 41.399993896484375, + "height": 12.239990234375 + }, + { + "x0": 398.1600036621094, + "y0": 351.7200012207031, + "x1": 451.79998779296875, + "y1": 364.67999267578125, + "width": 53.639984130859375, + "height": 12.959991455078125 + }, + { + "x0": 452.8800048828125, + "y0": 351.0, + "x1": 507.239990234375, + "y1": 363.9599914550781, + "width": 54.3599853515625, + "height": 12.959991455078125 + }, + { + "x0": 78.12000274658203, + "y0": 344.5199890136719, + "x1": 141.1199951171875, + "y1": 357.47998046875, + "width": 62.99999237060547, + "height": 12.959991455078125 + }, + { + "x0": 141.83999633789062, + "y0": 343.79998779296875, + "x1": 196.55999755859375, + "y1": 356.03997802734375, + "width": 54.720001220703125, + "height": 12.239990234375 + }, + { + "x0": 197.63999938964844, + "y0": 342.7200012207031, + "x1": 249.47999572753906, + "y1": 355.32000732421875, + "width": 51.839996337890625, + "height": 12.600006103515625 + }, + { + "x0": 250.1999969482422, + "y0": 341.6399841308594, + "x1": 302.0400085449219, + "y1": 354.239990234375, + "width": 51.84001159667969, + "height": 12.600006103515625 + }, + { + "x0": 302.760009765625, + "y0": 340.91998291015625, + "x1": 354.6000061035156, + "y1": 353.1600036621094, + "width": 51.839996337890625, + "height": 12.240020751953125 + }, + { + "x0": 355.67999267578125, + "y0": 340.1999816894531, + "x1": 396.7200012207031, + "y1": 352.44000244140625, + "width": 41.040008544921875, + "height": 12.240020751953125 + }, + { + "x0": 397.44000244140625, + "y0": 339.47998046875, + "x1": 451.79998779296875, + "y1": 351.7200012207031, + "width": 54.3599853515625, + "height": 12.240020751953125 + }, + { + "x0": 452.5199890136719, + "y0": 338.3999938964844, + "x1": 506.8800048828125, + "y1": 350.6399841308594, + "width": 54.360015869140625, + "height": 12.239990234375 + }, + { + "x0": 78.12000274658203, + "y0": 331.55999755859375, + "x1": 140.75999450683594, + "y1": 344.5199890136719, + "width": 62.639991760253906, + "height": 12.959991455078125 + }, + { + "x0": 141.47999572753906, + "y0": 330.8399963378906, + "x1": 196.55999755859375, + "y1": 343.44000244140625, + "width": 55.08000183105469, + "height": 12.600006103515625 + }, + { + "x0": 197.27999877929688, + "y0": 329.7599792480469, + "x1": 249.1199951171875, + "y1": 342.3599853515625, + "width": 51.839996337890625, + "height": 12.600006103515625 + }, + { + "x0": 250.1999969482422, + "y0": 329.03997802734375, + "x1": 301.67999267578125, + "y1": 341.6399841308594, + "width": 51.47999572753906, + "height": 12.600006103515625 + }, + { + "x0": 302.760009765625, + "y0": 327.96002197265625, + "x1": 354.239990234375, + "y1": 340.55999755859375, + "width": 51.47998046875, + "height": 12.5999755859375 + }, + { + "x0": 355.32000732421875, + "y0": 327.239990234375, + "x1": 396.3599853515625, + "y1": 339.8399963378906, + "width": 41.03997802734375, + "height": 12.600006103515625 + }, + { + "x0": 397.44000244140625, + "y0": 326.52001953125, + "x1": 451.44000244140625, + "y1": 339.1199951171875, + "width": 54.0, + "height": 12.5999755859375 + }, + { + "x0": 452.1600036621094, + "y0": 325.44000244140625, + "x1": 506.5199890136719, + "y1": 338.03997802734375, + "width": 54.3599853515625, + "height": 12.5999755859375 + }, + { + "x0": 77.4000015258789, + "y0": 293.03997802734375, + "x1": 140.39999389648438, + "y1": 331.55999755859375, + "width": 62.99999237060547, + "height": 38.52001953125 + }, + { + "x0": 141.47999572753906, + "y0": 317.8800048828125, + "x1": 196.1999969482422, + "y1": 330.47998046875, + "width": 54.720001220703125, + "height": 12.5999755859375 + }, + { + "x0": 196.9199981689453, + "y0": 317.15997314453125, + "x1": 249.1199951171875, + "y1": 329.39996337890625, + "width": 52.19999694824219, + "height": 12.239990234375 + }, + { + "x0": 249.83999633789062, + "y0": 316.08001708984375, + "x1": 301.32000732421875, + "y1": 328.67999267578125, + "width": 51.480010986328125, + "height": 12.5999755859375 + }, + { + "x0": 302.3999938964844, + "y0": 315.3599853515625, + "x1": 354.239990234375, + "y1": 327.96002197265625, + "width": 51.839996337890625, + "height": 12.60003662109375 + }, + { + "x0": 354.9599914550781, + "y0": 314.6400146484375, + "x1": 396.3599853515625, + "y1": 326.8800048828125, + "width": 41.399993896484375, + "height": 12.239990234375 + }, + { + "x0": 397.0799865722656, + "y0": 313.55999755859375, + "x1": 451.0799865722656, + "y1": 326.15997314453125, + "width": 54.0, + "height": 12.5999755859375 + }, + { + "x0": 452.1600036621094, + "y0": 312.8399658203125, + "x1": 506.5199890136719, + "y1": 325.08001708984375, + "width": 54.3599853515625, + "height": 12.24005126953125 + }, + { + "x0": 141.1199951171875, + "y0": 304.91998291015625, + "x1": 195.83999633789062, + "y1": 317.8800048828125, + "width": 54.720001220703125, + "height": 12.96002197265625 + }, + { + "x0": 196.9199981689453, + "y0": 304.20001220703125, + "x1": 248.75999450683594, + "y1": 316.79998779296875, + "width": 51.839996337890625, + "height": 12.5999755859375 + }, + { + "x0": 249.47999572753906, + "y0": 303.47998046875, + "x1": 301.32000732421875, + "y1": 315.719970703125, + "width": 51.84001159667969, + "height": 12.239990234375 + }, + { + "x0": 302.3999938964844, + "y0": 302.39996337890625, + "x1": 353.8800048828125, + "y1": 315.0, + "width": 51.480010986328125, + "height": 12.60003662109375 + }, + { + "x0": 354.9599914550781, + "y0": 301.67999267578125, + "x1": 396.3599853515625, + "y1": 314.27996826171875, + "width": 41.399993896484375, + "height": 12.5999755859375 + }, + { + "x0": 397.0799865722656, + "y0": 300.5999755859375, + "x1": 451.0799865722656, + "y1": 313.55999755859375, + "width": 54.0, + "height": 12.96002197265625 + }, + { + "x0": 451.79998779296875, + "y0": 299.8800048828125, + "x1": 506.1600036621094, + "y1": 312.47998046875, + "width": 54.360015869140625, + "height": 12.5999755859375 + }, + { + "x0": 140.75999450683594, + "y0": 291.96002197265625, + "x1": 195.83999633789062, + "y1": 304.91998291015625, + "width": 55.08000183105469, + "height": 12.9599609375 + }, + { + "x0": 196.55999755859375, + "y0": 291.239990234375, + "x1": 248.75999450683594, + "y1": 304.20001220703125, + "width": 52.19999694824219, + "height": 12.96002197265625 + }, + { + "x0": 249.47999572753906, + "y0": 290.52001953125, + "x1": 301.32000732421875, + "y1": 303.1199951171875, + "width": 51.84001159667969, + "height": 12.5999755859375 + }, + { + "x0": 302.0400085449219, + "y0": 289.44000244140625, + "x1": 353.8800048828125, + "y1": 302.03997802734375, + "width": 51.839996337890625, + "height": 12.5999755859375 + }, + { + "x0": 354.6000061035156, + "y0": 288.719970703125, + "x1": 396.3599853515625, + "y1": 301.32000732421875, + "width": 41.759979248046875, + "height": 12.60003662109375 + }, + { + "x0": 396.7200012207031, + "y0": 287.6400146484375, + "x1": 451.0799865722656, + "y1": 300.5999755859375, + "width": 54.3599853515625, + "height": 12.9599609375 + }, + { + "x0": 451.44000244140625, + "y0": 286.91998291015625, + "x1": 505.79998779296875, + "y1": 299.52001953125, + "width": 54.3599853515625, + "height": 12.60003662109375 + }, + { + "x0": 77.04000091552734, + "y0": 267.47998046875, + "x1": 139.67999267578125, + "y1": 293.03997802734375, + "width": 62.639991760253906, + "height": 25.55999755859375 + }, + { + "x0": 140.75999450683594, + "y0": 279.3599853515625, + "x1": 195.83999633789062, + "y1": 291.96002197265625, + "width": 55.08000183105469, + "height": 12.60003662109375 + }, + { + "x0": 195.83999633789062, + "y0": 278.27996826171875, + "x1": 248.75999450683594, + "y1": 290.8800048828125, + "width": 52.91999816894531, + "height": 12.60003662109375 + }, + { + "x0": 249.1199951171875, + "y0": 277.55999755859375, + "x1": 301.32000732421875, + "y1": 290.15997314453125, + "width": 52.20001220703125, + "height": 12.5999755859375 + }, + { + "x0": 301.67999267578125, + "y0": 276.8399658203125, + "x1": 353.8800048828125, + "y1": 289.08001708984375, + "width": 52.20001220703125, + "height": 12.24005126953125 + }, + { + "x0": 354.239990234375, + "y0": 276.1199951171875, + "x1": 396.3599853515625, + "y1": 288.3599853515625, + "width": 42.1199951171875, + "height": 12.239990234375 + }, + { + "x0": 396.7200012207031, + "y0": 275.03997802734375, + "x1": 451.0799865722656, + "y1": 287.6400146484375, + "width": 54.3599853515625, + "height": 12.60003662109375 + }, + { + "x0": 451.44000244140625, + "y0": 273.96002197265625, + "x1": 505.79998779296875, + "y1": 286.55999755859375, + "width": 54.3599853515625, + "height": 12.5999755859375 + }, + { + "x0": 140.39999389648438, + "y0": 266.39996337890625, + "x1": 195.1199951171875, + "y1": 279.0, + "width": 54.720001220703125, + "height": 12.60003662109375 + }, + { + "x0": 195.83999633789062, + "y0": 265.32000732421875, + "x1": 248.39999389648438, + "y1": 278.27996826171875, + "width": 52.55999755859375, + "height": 12.9599609375 + }, + { + "x0": 248.75999450683594, + "y0": 264.5999755859375, + "x1": 300.6000061035156, + "y1": 277.20001220703125, + "width": 51.84001159667969, + "height": 12.60003662109375 + }, + { + "x0": 301.32000732421875, + "y0": 263.8800048828125, + "x1": 353.1600036621094, + "y1": 276.1199951171875, + "width": 51.839996337890625, + "height": 12.239990234375 + }, + { + "x0": 353.8800048828125, + "y0": 263.15997314453125, + "x1": 395.6400146484375, + "y1": 275.39996337890625, + "width": 41.760009765625, + "height": 12.239990234375 + }, + { + "x0": 396.0, + "y0": 262.08001708984375, + "x1": 450.3599853515625, + "y1": 274.67999267578125, + "width": 54.3599853515625, + "height": 12.5999755859375 + }, + { + "x0": 450.7200012207031, + "y0": 261.0, + "x1": 505.44000244140625, + "y1": 273.96002197265625, + "width": 54.720001220703125, + "height": 12.96002197265625 + }, + { + "x0": 76.68000030517578, + "y0": 254.52001953125, + "x1": 139.32000732421875, + "y1": 267.47998046875, + "width": 62.64000701904297, + "height": 12.9599609375 + }, + { + "x0": 140.0399932861328, + "y0": 253.44000244140625, + "x1": 195.1199951171875, + "y1": 266.03997802734375, + "width": 55.08000183105469, + "height": 12.5999755859375 + }, + { + "x0": 195.83999633789062, + "y0": 252.3599853515625, + "x1": 248.0399932861328, + "y1": 265.32000732421875, + "width": 52.19999694824219, + "height": 12.96002197265625 + }, + { + "x0": 248.75999450683594, + "y0": 251.6400146484375, + "x1": 300.239990234375, + "y1": 264.239990234375, + "width": 51.47999572753906, + "height": 12.5999755859375 + }, + { + "x0": 301.32000732421875, + "y0": 250.91998291015625, + "x1": 353.1600036621094, + "y1": 263.52001953125, + "width": 51.839996337890625, + "height": 12.60003662109375 + }, + { + "x0": 353.8800048828125, + "y0": 250.20001220703125, + "x1": 395.2799987792969, + "y1": 262.44000244140625, + "width": 41.399993896484375, + "height": 12.239990234375 + }, + { + "x0": 396.0, + "y0": 249.1199951171875, + "x1": 450.3599853515625, + "y1": 261.719970703125, + "width": 54.3599853515625, + "height": 12.5999755859375 + }, + { + "x0": 450.7200012207031, + "y0": 248.03997802734375, + "x1": 505.0799865722656, + "y1": 261.0, + "width": 54.3599853515625, + "height": 12.96002197265625 + }, + { + "x0": 76.68000030517578, + "y0": 241.55999755859375, + "x1": 138.9600067138672, + "y1": 254.52001953125, + "width": 62.280006408691406, + "height": 12.96002197265625 + }, + { + "x0": 140.0399932861328, + "y0": 240.8399658203125, + "x1": 194.75999450683594, + "y1": 253.08001708984375, + "width": 54.720001220703125, + "height": 12.24005126953125 + }, + { + "x0": 195.47999572753906, + "y0": 239.760009765625, + "x1": 247.67999267578125, + "y1": 252.3599853515625, + "width": 52.19999694824219, + "height": 12.5999755859375 + }, + { + "x0": 248.39999389648438, + "y0": 239.03997802734375, + "x1": 300.239990234375, + "y1": 251.27996826171875, + "width": 51.839996337890625, + "height": 12.239990234375 + }, + { + "x0": 300.9599914550781, + "y0": 237.96002197265625, + "x1": 352.79998779296875, + "y1": 250.55999755859375, + "width": 51.839996337890625, + "height": 12.5999755859375 + }, + { + "x0": 353.5199890136719, + "y0": 237.239990234375, + "x1": 394.9200134277344, + "y1": 249.47998046875, + "width": 41.4000244140625, + "height": 12.239990234375 + }, + { + "x0": 396.0, + "y0": 236.52001953125, + "x1": 450.0, + "y1": 248.760009765625, + "width": 54.0, + "height": 12.239990234375 + }, + { + "x0": 450.7200012207031, + "y0": 235.79998779296875, + "x1": 505.0799865722656, + "y1": 248.03997802734375, + "width": 54.3599853515625, + "height": 12.239990234375 + }, + { + "x0": 75.23999786376953, + "y0": 178.20001220703125, + "x1": 138.9600067138672, + "y1": 241.55999755859375, + "width": 63.720008850097656, + "height": 63.3599853515625 + }, + { + "x0": 139.67999267578125, + "y0": 227.8800048828125, + "x1": 194.39999389648438, + "y1": 240.47998046875, + "width": 54.720001220703125, + "height": 12.5999755859375 + }, + { + "x0": 195.47999572753906, + "y0": 227.15997314453125, + "x1": 247.67999267578125, + "y1": 239.39996337890625, + "width": 52.19999694824219, + "height": 12.239990234375 + }, + { + "x0": 248.0399932861328, + "y0": 226.08001708984375, + "x1": 299.8800048828125, + "y1": 238.67999267578125, + "width": 51.84001159667969, + "height": 12.5999755859375 + }, + { + "x0": 300.9599914550781, + "y0": 225.3599853515625, + "x1": 352.44000244140625, + "y1": 237.96002197265625, + "width": 51.480010986328125, + "height": 12.60003662109375 + }, + { + "x0": 353.5199890136719, + "y0": 224.6400146484375, + "x1": 394.9200134277344, + "y1": 236.8800048828125, + "width": 41.4000244140625, + "height": 12.239990234375 + }, + { + "x0": 395.6400146484375, + "y0": 223.91998291015625, + "x1": 449.6400146484375, + "y1": 236.52001953125, + "width": 54.0, + "height": 12.60003662109375 + }, + { + "x0": 450.7200012207031, + "y0": 222.8399658203125, + "x1": 504.7200012207031, + "y1": 235.44000244140625, + "width": 54.0, + "height": 12.60003662109375 + }, + { + "x0": 139.32000732421875, + "y0": 215.27996826171875, + "x1": 194.39999389648438, + "y1": 227.52001953125, + "width": 55.079986572265625, + "height": 12.24005126953125 + }, + { + "x0": 195.1199951171875, + "y0": 214.55999755859375, + "x1": 247.32000732421875, + "y1": 226.79998779296875, + "width": 52.20001220703125, + "height": 12.239990234375 + }, + { + "x0": 248.0399932861328, + "y0": 213.47998046875, + "x1": 299.5199890136719, + "y1": 225.719970703125, + "width": 51.47999572753906, + "height": 12.239990234375 + }, + { + "x0": 300.6000061035156, + "y0": 212.760009765625, + "x1": 352.44000244140625, + "y1": 225.0, + "width": 51.839996337890625, + "height": 12.239990234375 + }, + { + "x0": 353.1600036621094, + "y0": 212.03997802734375, + "x1": 394.55999755859375, + "y1": 224.27996826171875, + "width": 41.399993896484375, + "height": 12.239990234375 + }, + { + "x0": 395.2799987792969, + "y0": 211.32000732421875, + "x1": 449.6400146484375, + "y1": 223.55999755859375, + "width": 54.360015869140625, + "height": 12.239990234375 + }, + { + "x0": 450.3599853515625, + "y0": 210.239990234375, + "x1": 504.3599853515625, + "y1": 222.8399658203125, + "width": 54.0, + "height": 12.5999755859375 + }, + { + "x0": 139.32000732421875, + "y0": 202.67999267578125, + "x1": 194.0399932861328, + "y1": 214.91998291015625, + "width": 54.71998596191406, + "height": 12.239990234375 + }, + { + "x0": 194.75999450683594, + "y0": 201.96002197265625, + "x1": 246.9600067138672, + "y1": 214.20001220703125, + "width": 52.20001220703125, + "height": 12.239990234375 + }, + { + "x0": 248.0399932861328, + "y0": 200.8800048828125, + "x1": 299.5199890136719, + "y1": 213.47998046875, + "width": 51.47999572753906, + "height": 12.5999755859375 + }, + { + "x0": 300.239990234375, + "y0": 200.15997314453125, + "x1": 352.0799865722656, + "y1": 212.39996337890625, + "width": 51.839996337890625, + "height": 12.239990234375 + }, + { + "x0": 352.79998779296875, + "y0": 199.44000244140625, + "x1": 394.20001220703125, + "y1": 211.67999267578125, + "width": 41.4000244140625, + "height": 12.239990234375 + }, + { + "x0": 395.2799987792969, + "y0": 198.3599853515625, + "x1": 448.9200134277344, + "y1": 210.96002197265625, + "width": 53.6400146484375, + "height": 12.60003662109375 + }, + { + "x0": 450.0, + "y0": 197.6400146484375, + "x1": 504.3599853515625, + "y1": 209.8800048828125, + "width": 54.3599853515625, + "height": 12.239990234375 + }, + { + "x0": 138.9600067138672, + "y0": 190.08001708984375, + "x1": 194.0399932861328, + "y1": 202.32000732421875, + "width": 55.079986572265625, + "height": 12.239990234375 + }, + { + "x0": 194.75999450683594, + "y0": 189.0, + "x1": 246.9600067138672, + "y1": 201.239990234375, + "width": 52.20001220703125, + "height": 12.239990234375 + }, + { + "x0": 247.67999267578125, + "y0": 188.27996826171875, + "x1": 299.1600036621094, + "y1": 200.52001953125, + "width": 51.480010986328125, + "height": 12.24005126953125 + }, + { + "x0": 300.239990234375, + "y0": 187.55999755859375, + "x1": 351.7200012207031, + "y1": 199.79998779296875, + "width": 51.480010986328125, + "height": 12.239990234375 + }, + { + "x0": 352.79998779296875, + "y0": 186.8399658203125, + "x1": 394.20001220703125, + "y1": 199.08001708984375, + "width": 41.4000244140625, + "height": 12.24005126953125 + }, + { + "x0": 394.9200134277344, + "y0": 185.760009765625, + "x1": 448.9200134277344, + "y1": 198.3599853515625, + "width": 54.0, + "height": 12.5999755859375 + }, + { + "x0": 450.0, + "y0": 185.03997802734375, + "x1": 504.0, + "y1": 197.27996826171875, + "width": 54.0, + "height": 12.239990234375 + }, + { + "x0": 138.60000610351562, + "y0": 177.1199951171875, + "x1": 194.0399932861328, + "y1": 189.719970703125, + "width": 55.43998718261719, + "height": 12.5999755859375 + }, + { + "x0": 194.75999450683594, + "y0": 176.39996337890625, + "x1": 246.9600067138672, + "y1": 189.0, + "width": 52.20001220703125, + "height": 12.60003662109375 + }, + { + "x0": 247.32000732421875, + "y0": 175.32000732421875, + "x1": 299.1600036621094, + "y1": 187.91998291015625, + "width": 51.839996337890625, + "height": 12.5999755859375 + }, + { + "x0": 299.8800048828125, + "y0": 174.5999755859375, + "x1": 351.7200012207031, + "y1": 187.20001220703125, + "width": 51.839996337890625, + "height": 12.60003662109375 + }, + { + "x0": 352.44000244140625, + "y0": 173.8800048828125, + "x1": 394.20001220703125, + "y1": 186.1199951171875, + "width": 41.760009765625, + "height": 12.239990234375 + }, + { + "x0": 394.55999755859375, + "y0": 173.15997314453125, + "x1": 448.9200134277344, + "y1": 185.39996337890625, + "width": 54.360015869140625, + "height": 12.239990234375 + }, + { + "x0": 449.6400146484375, + "y0": 172.08001708984375, + "x1": 504.0, + "y1": 184.67999267578125, + "width": 54.3599853515625, + "height": 12.5999755859375 + } + ] + } + ] +} \ No newline at end of file