Compare commits
8 Commits
master
...
RED-5381-T
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad455455df | ||
|
|
b10958cb6a | ||
|
|
4be5487e64 | ||
|
|
c23dd86b6d | ||
|
|
15c131d6c4 | ||
|
|
248e4100c0 | ||
|
|
2f72dc895c | ||
|
|
5f0b7d8344 |
@ -1,16 +1,18 @@
|
|||||||
package com.iqser.red.service.redaction.v1.server.classification.model;
|
package com.iqser.red.service.redaction.v1.server.classification.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
|
|
||||||
|
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
||||||
import com.iqser.red.service.redaction.v1.server.redaction.model.PdfImage;
|
import com.iqser.red.service.redaction.v1.server.redaction.model.PdfImage;
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Rectangle;
|
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class Page {
|
public class Page {
|
||||||
@ -33,6 +35,7 @@ public class Page {
|
|||||||
private StringFrequencyCounter fontStyleCounter = new StringFrequencyCounter();
|
private StringFrequencyCounter fontStyleCounter = new StringFrequencyCounter();
|
||||||
|
|
||||||
private double cropBoxArea;
|
private double cropBoxArea;
|
||||||
|
private PDRectangle cropBox;
|
||||||
|
|
||||||
|
|
||||||
public boolean isRotated() {
|
public boolean isRotated() {
|
||||||
|
|||||||
@ -1,8 +1,12 @@
|
|||||||
package com.iqser.red.service.redaction.v1.server.classification.model;
|
package com.iqser.red.service.redaction.v1.server.classification.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.dslplatform.json.CompiledJson;
|
import com.dslplatform.json.CompiledJson;
|
||||||
import com.dslplatform.json.JsonAttribute;
|
import com.dslplatform.json.JsonAttribute;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.parsing.model.TextDirection;
|
||||||
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
||||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.TextNormalizationUtilities;
|
import com.iqser.red.service.redaction.v1.server.redaction.utils.TextNormalizationUtilities;
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
|
||||||
@ -12,9 +16,6 @@ import lombok.Builder;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder
|
@Builder
|
||||||
@Data
|
@Data
|
||||||
@ -50,6 +51,94 @@ public class TextBlock extends AbstractTextContainer {
|
|||||||
private String classification;
|
private String classification;
|
||||||
|
|
||||||
|
|
||||||
|
private TextDirection getDir() {
|
||||||
|
|
||||||
|
return sequences.get(0).getDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private float getPageHeight() {
|
||||||
|
|
||||||
|
return sequences.get(0).getPageHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private float getPageWidth() {
|
||||||
|
|
||||||
|
return sequences.get(0).getPageWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@JsonAttribute(ignore = true)
|
||||||
|
public float getX1() {
|
||||||
|
|
||||||
|
if (getDir().getDegrees() == 90) {
|
||||||
|
return minY;
|
||||||
|
} else if (getDir().getDegrees() == 180) {
|
||||||
|
return getPageWidth() - maxX;
|
||||||
|
|
||||||
|
} else if (getDir().getDegrees() == 270) {
|
||||||
|
|
||||||
|
return getPageWidth() - maxY;
|
||||||
|
} else {
|
||||||
|
return minX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@JsonAttribute(ignore = true)
|
||||||
|
public float getX2() {
|
||||||
|
|
||||||
|
if (getDir().getDegrees() == 90) {
|
||||||
|
return maxY;
|
||||||
|
} else if (getDir().getDegrees() == 180) {
|
||||||
|
return getPageWidth() - minX;
|
||||||
|
} else if (getDir().getDegrees() == 270) {
|
||||||
|
return getPageWidth() - minY;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return maxX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@JsonAttribute(ignore = true)
|
||||||
|
public float getY1() {
|
||||||
|
|
||||||
|
if (getDir().getDegrees() == 90) {
|
||||||
|
return minX;
|
||||||
|
} else if (getDir().getDegrees() == 180) {
|
||||||
|
return maxY;
|
||||||
|
|
||||||
|
} else if (getDir().getDegrees() == 270) {
|
||||||
|
return getPageHeight() - maxX;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return getPageHeight() - maxY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@JsonAttribute(ignore = true)
|
||||||
|
public float getY2() {
|
||||||
|
|
||||||
|
if (getDir().getDegrees() == 90) {
|
||||||
|
return maxX;
|
||||||
|
} else if (getDir().getDegrees() == 180) {
|
||||||
|
|
||||||
|
return minY;
|
||||||
|
} else if (getDir().getDegrees() == 270) {
|
||||||
|
return getPageHeight() - minX;
|
||||||
|
} else {
|
||||||
|
return getPageHeight() - minY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public TextBlock(float minX, float maxX, float minY, float maxY, List<TextPositionSequence> sequences, int rotation) {
|
public TextBlock(float minX, float maxX, float minY, float maxY, List<TextPositionSequence> sequences, int rotation) {
|
||||||
|
|
||||||
this.minX = minX;
|
this.minX = minX;
|
||||||
|
|||||||
@ -7,18 +7,21 @@ import java.util.Comparator;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.iqser.red.service.redaction.v1.model.Point;
|
||||||
|
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.FloatFrequencyCounter;
|
import com.iqser.red.service.redaction.v1.server.classification.model.FloatFrequencyCounter;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.Orientation;
|
import com.iqser.red.service.redaction.v1.server.classification.model.Orientation;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.Page;
|
import com.iqser.red.service.redaction.v1.server.classification.model.Page;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.StringFrequencyCounter;
|
import com.iqser.red.service.redaction.v1.server.classification.model.StringFrequencyCounter;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.classification.utils.CoordSystemHelper;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.utils.PositionUtils;
|
import com.iqser.red.service.redaction.v1.server.classification.utils.PositionUtils;
|
||||||
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Cell;
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Cell;
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Rectangle;
|
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Ruling;
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Ruling;
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Table;
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Table;
|
||||||
|
|
||||||
@ -29,9 +32,9 @@ public class BlockificationService {
|
|||||||
static final float THRESHOLD = 1f;
|
static final float THRESHOLD = 1f;
|
||||||
|
|
||||||
|
|
||||||
public Page blockify(List<TextPositionSequence> textPositions, List<Ruling> horizontalRulingLines, List<Ruling> verticalRulingLines) {
|
public Page blockify(List<TextPositionSequence> textPositions, List<Ruling> horizontalRulingLines, List<Ruling> verticalRulingLines, int rotation, PDRectangle cropbox) {
|
||||||
|
|
||||||
sortRotatedSequences(textPositions);
|
// sortRotatedSequences(textPositions);
|
||||||
|
|
||||||
List<TextPositionSequence> chunkWords = new ArrayList<>();
|
List<TextPositionSequence> chunkWords = new ArrayList<>();
|
||||||
List<AbstractTextContainer> chunkBlockList1 = new ArrayList<>();
|
List<AbstractTextContainer> chunkBlockList1 = new ArrayList<>();
|
||||||
@ -43,23 +46,45 @@ public class BlockificationService {
|
|||||||
Float splitX1 = null;
|
Float splitX1 = null;
|
||||||
for (TextPositionSequence word : textPositions) {
|
for (TextPositionSequence word : textPositions) {
|
||||||
|
|
||||||
boolean lineSeparation = minY - word.getY2() > word.getHeight() * 1.25;
|
boolean lineSeparation = word.getY1() - maxY > word.getHeight() * 1.25;
|
||||||
boolean startFromTop = word.getY1() > maxY + word.getHeight();
|
boolean startFromTop = prev != null && word.getY1() < prev.getY1() - prev.getTextHeight();
|
||||||
boolean splitByX = prev != null && maxX + 50 < word.getX1() && prev.getY1() == word.getY1();
|
boolean splitByX = prev != null && maxX + 50 < word.getX1() && prev.getY1() == word.getY1();
|
||||||
|
boolean xIsBeforeFirstX = prev != null && word.getX1() < minX;
|
||||||
boolean newLineAfterSplit = prev != null && word.getY1() != prev.getY1() && wasSplitted && splitX1 != word.getX1();
|
boolean newLineAfterSplit = prev != null && word.getY1() != prev.getY1() && wasSplitted && splitX1 != word.getX1();
|
||||||
boolean splittedByRuling = isSplittedByRuling(maxX, minY, word.getX1(), word.getY1(), verticalRulingLines) || isSplittedByRuling(minX,
|
boolean splittedByRuling = isSplittedByRuling(maxX,
|
||||||
|
minY,
|
||||||
|
word.getX1(),
|
||||||
|
word.getY1(),
|
||||||
|
verticalRulingLines,
|
||||||
|
word.getDir().getDegrees(),
|
||||||
|
cropbox.getWidth(),
|
||||||
|
cropbox.getHeight()) || isSplittedByRuling(minX,
|
||||||
minY,
|
minY,
|
||||||
word.getX1(),
|
word.getX1(),
|
||||||
word.getY2(),
|
word.getY2(),
|
||||||
horizontalRulingLines)
|
horizontalRulingLines,
|
||||||
|
word.getDir().getDegrees(),
|
||||||
|
cropbox.getWidth(),
|
||||||
|
cropbox.getHeight())
|
||||||
|
|
||||||
|| isSplittedByRuling(maxX, minY, word.getX1(), word.getY1(), horizontalRulingLines) || isSplittedByRuling(minX,
|
|| isSplittedByRuling(maxX,
|
||||||
|
minY,
|
||||||
|
word.getX1(),
|
||||||
|
word.getY1(),
|
||||||
|
horizontalRulingLines,
|
||||||
|
word.getDir().getDegrees(),
|
||||||
|
cropbox.getWidth(),
|
||||||
|
cropbox.getHeight()) || isSplittedByRuling(minX,
|
||||||
minY,
|
minY,
|
||||||
word.getX1(),
|
word.getX1(),
|
||||||
word.getY2(),
|
word.getY2(),
|
||||||
verticalRulingLines);
|
verticalRulingLines,
|
||||||
|
word.getDir().getDegrees(),
|
||||||
|
cropbox.getWidth(),
|
||||||
|
cropbox.getHeight());
|
||||||
|
boolean splitByDir = prev != null && !prev.getDir().equals(word.getDir());
|
||||||
|
|
||||||
if (prev != null && (lineSeparation || startFromTop || splitByX || newLineAfterSplit || splittedByRuling)) {
|
if (prev != null && (lineSeparation || startFromTop || xIsBeforeFirstX || splitByX || splitByDir || splittedByRuling)) {
|
||||||
|
|
||||||
Orientation prevOrientation = null;
|
Orientation prevOrientation = null;
|
||||||
if (!chunkBlockList1.isEmpty()) {
|
if (!chunkBlockList1.isEmpty()) {
|
||||||
@ -209,10 +234,18 @@ public class BlockificationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean isSplittedByRuling(float previousX2, float previousY1, float currentX1, float currentY1, List<Ruling> rulingLines) {
|
private boolean isSplittedByRuling(float previousX2,
|
||||||
|
float previousY1,
|
||||||
|
float currentX1,
|
||||||
|
float currentY1,
|
||||||
|
List<Ruling> rulingLines,
|
||||||
|
float rotation,
|
||||||
|
float pageWidth,
|
||||||
|
float pageHeight) {
|
||||||
|
|
||||||
for (Ruling ruling : rulingLines) {
|
for (Ruling ruling : rulingLines) {
|
||||||
if (ruling.intersectsLine(previousX2, previousY1, currentX1, currentY1)) {
|
var line = CoordSystemHelper.convertToDirAdj(ruling, rotation, pageWidth, pageHeight);
|
||||||
|
if (line.intersectsLine(previousX2, previousY1, currentX1, currentY1)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,19 +282,33 @@ public class BlockificationService {
|
|||||||
if (documentFontSizeCounter.getMostPopular() != null) {
|
if (documentFontSizeCounter.getMostPopular() != null) {
|
||||||
if (textBlock.getMostPopularWordFontSize() >= documentFontSizeCounter.getMostPopular()) {
|
if (textBlock.getMostPopularWordFontSize() >= documentFontSizeCounter.getMostPopular()) {
|
||||||
|
|
||||||
if (textBlock.getMinX() < minX) {
|
if (page.getCropBox().getWidth() > page.getCropBox().getHeight() && page.getRotation() != 0) {
|
||||||
minX = textBlock.getMinX();
|
if (textBlock.getY1() < minX) {
|
||||||
|
minX = textBlock.getY1();
|
||||||
|
}
|
||||||
|
if (textBlock.getY2() > maxX) {
|
||||||
|
maxX = textBlock.getY2();
|
||||||
|
}
|
||||||
|
if (textBlock.getX1() < minY) {
|
||||||
|
minY = textBlock.getX1();
|
||||||
|
}
|
||||||
|
if (textBlock.getX2() > maxY) {
|
||||||
|
maxY = textBlock.getX2();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (textBlock.getX1() < minX) {
|
||||||
|
minX = textBlock.getX1();
|
||||||
|
}
|
||||||
|
if (textBlock.getX2() > maxX) {
|
||||||
|
maxX = textBlock.getX2();
|
||||||
|
}
|
||||||
|
if (textBlock.getY1() < minY) {
|
||||||
|
minY = textBlock.getY1();
|
||||||
|
}
|
||||||
|
if (textBlock.getY2() > maxY) {
|
||||||
|
maxY = textBlock.getY2();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (textBlock.getMaxX() > maxX) {
|
|
||||||
maxX = textBlock.getMaxX();
|
|
||||||
}
|
|
||||||
if (textBlock.getMinY() < minY) {
|
|
||||||
minY = textBlock.getMinY();
|
|
||||||
}
|
|
||||||
if (textBlock.getMaxY() > maxY) {
|
|
||||||
maxY = textBlock.getMaxY();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,45 +322,42 @@ public class BlockificationService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (TextBlock textBlock : cell.getTextBlocks()) {
|
for (TextBlock textBlock : cell.getTextBlocks()) {
|
||||||
if (textBlock.getMinX() < minX) {
|
if (page.getCropBox().getWidth() > page.getCropBox().getHeight() && page.getRotation() != 0) {
|
||||||
minX = textBlock.getMinX();
|
if (textBlock.getY1() < minX) {
|
||||||
}
|
minX = textBlock.getMinY();
|
||||||
if (textBlock.getMaxX() > maxX) {
|
}
|
||||||
maxX = textBlock.getMaxX();
|
if (textBlock.getY2() > maxX) {
|
||||||
}
|
maxX = textBlock.getY2();
|
||||||
if (textBlock.getMinY() < minY) {
|
}
|
||||||
minY = textBlock.getMinY();
|
if (textBlock.getX1() < minY) {
|
||||||
}
|
minY = textBlock.getX1();
|
||||||
if (textBlock.getMaxY() > maxY) {
|
}
|
||||||
maxY = textBlock.getMaxY();
|
if (textBlock.getX2() > maxY) {
|
||||||
|
maxY = textBlock.getX2();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (textBlock.getX1() < minX) {
|
||||||
|
minX = textBlock.getX1();
|
||||||
|
}
|
||||||
|
if (textBlock.getX2() > maxX) {
|
||||||
|
maxX = textBlock.getX2();
|
||||||
|
}
|
||||||
|
if (textBlock.getY1() < minY) {
|
||||||
|
minY = textBlock.getY1();
|
||||||
|
}
|
||||||
|
if (textBlock.getY2() > maxY) {
|
||||||
|
maxY = textBlock.getY2();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
System.out.println("Page: " + page.getPageNumber() + " Landscape: " + page.isLandscape() + " MinX: " + minX + " MaxX: " + maxX + " MinY: " + minY + " MaxY: " + maxY);
|
||||||
|
|
||||||
}
|
}
|
||||||
return new Rectangle(minY, minX, maxX - minX, maxY - minY);
|
return new Rectangle(new Point(minX, minY), maxX - minX, maxY - minY, 0);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void sortRotatedSequences(List<TextPositionSequence> sequences) {
|
|
||||||
|
|
||||||
List<TextPositionSequence> rotatedWords = new ArrayList<>();
|
|
||||||
Iterator<TextPositionSequence> itty = sequences.iterator();
|
|
||||||
while (itty.hasNext()) {
|
|
||||||
var pos = itty.next();
|
|
||||||
if (pos.getTextPositions().get(0).getDir() == 270) {
|
|
||||||
rotatedWords.add(pos);
|
|
||||||
itty.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rotatedWords.isEmpty() && !sequences.isEmpty()) {
|
|
||||||
rotatedWords.sort(Comparator.comparing(TextPositionSequence::getX1));
|
|
||||||
}
|
|
||||||
sequences.addAll(rotatedWords);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,21 @@
|
|||||||
package com.iqser.red.service.redaction.v1.server.classification.service;
|
package com.iqser.red.service.redaction.v1.server.classification.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.iqser.red.service.redaction.v1.model.Point;
|
||||||
|
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.Document;
|
import com.iqser.red.service.redaction.v1.server.classification.model.Document;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.Page;
|
import com.iqser.red.service.redaction.v1.server.classification.model.Page;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.utils.PositionUtils;
|
import com.iqser.red.service.redaction.v1.server.classification.utils.PositionUtils;
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Rectangle;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@ -34,7 +35,23 @@ public class ClassificationService {
|
|||||||
|
|
||||||
for (Page page : document.getPages()) {
|
for (Page page : document.getPages()) {
|
||||||
Rectangle btf = page.isLandscape() ? landscapeBodyTextFrame : bodyTextFrame;
|
Rectangle btf = page.isLandscape() ? landscapeBodyTextFrame : bodyTextFrame;
|
||||||
|
|
||||||
|
if (page.getCropBox().getWidth() > page.getCropBox().getHeight() && page.getRotation() == 270) {
|
||||||
|
btf = new Rectangle(new Point(btf.getTopLeft().getY(), page.getCropBox().getHeight() - btf.getTopLeft().getX() - btf.getWidth()),
|
||||||
|
btf.getHeight(),
|
||||||
|
btf.getWidth(),
|
||||||
|
0);
|
||||||
|
} else if (page.getCropBox().getWidth() > page.getCropBox().getHeight() && page.getRotation() != 0) {
|
||||||
|
btf = new Rectangle(new Point(btf.getTopLeft().getY(), btf.getTopLeft().getX()), btf.getHeight(), btf.getWidth(), 0);
|
||||||
|
} else if (page.getRotation() == 180) {
|
||||||
|
btf = new Rectangle(new Point(btf.getTopLeft().getX(), page.getCropBox().getHeight() - btf.getTopLeft().getY() - btf.getHeight()),
|
||||||
|
btf.getWidth(),
|
||||||
|
btf.getHeight(),
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
page.setBodyTextFrame(btf);
|
page.setBodyTextFrame(btf);
|
||||||
|
|
||||||
classifyPage(btf, page, document, headlineFontSizes);
|
classifyPage(btf, page, document, headlineFontSizes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,16 +69,25 @@ public class ClassificationService {
|
|||||||
|
|
||||||
public void classifyBlock(TextBlock textBlock, Rectangle bodyTextFrame, Page page, Document document, List<Float> headlineFontSizes) {
|
public void classifyBlock(TextBlock textBlock, Rectangle bodyTextFrame, Page page, Document document, List<Float> headlineFontSizes) {
|
||||||
|
|
||||||
if (document.getFontSizeCounter().getMostPopular() == null) {
|
System.out.println("Page: " + page.getPageNumber() + " rotation " + page.getRotation() + " B " + textBlock.getSequences().get(0).getDir());
|
||||||
textBlock.setClassification("Other");
|
|
||||||
return;
|
// if (document.getFontSizeCounter().getMostPopular() == null) {
|
||||||
}
|
// textBlock.setClassification("Other");
|
||||||
if (PositionUtils.isOverBodyTextFrame(bodyTextFrame, textBlock, page.isRotated()) && (document.getFontSizeCounter()
|
// return;
|
||||||
.getMostPopular() == null || textBlock.getHighestFontSize() <= document.getFontSizeCounter().getMostPopular())) {
|
// }
|
||||||
|
if (PositionUtils.isOverBodyTextFrame(bodyTextFrame, textBlock, page.getRotation()))
|
||||||
|
// && (document.getFontSizeCounter()
|
||||||
|
// .getMostPopular() == null || textBlock.getHighestFontSize() <= document.getFontSizeCounter()
|
||||||
|
// .getMostPopular()))
|
||||||
|
{
|
||||||
textBlock.setClassification("Header");
|
textBlock.setClassification("Header");
|
||||||
|
|
||||||
} else if (PositionUtils.isUnderBodyTextFrame(bodyTextFrame, textBlock) && (document.getFontSizeCounter()
|
} else if (PositionUtils.isUnderBodyTextFrame(bodyTextFrame, textBlock, page.getRotation()))
|
||||||
.getMostPopular() == null || textBlock.getHighestFontSize() <= document.getFontSizeCounter().getMostPopular())) {
|
|
||||||
|
// && (document.getFontSizeCounter()
|
||||||
|
// .getMostPopular() == null || textBlock.getHighestFontSize() <= document.getFontSizeCounter()
|
||||||
|
// .getMostPopular()))
|
||||||
|
{
|
||||||
textBlock.setClassification("Footer");
|
textBlock.setClassification("Footer");
|
||||||
} else if (page.getPageNumber() == 1 && (!PositionUtils.isTouchingUnderBodyTextFrame(bodyTextFrame,
|
} else if (page.getPageNumber() == 1 && (!PositionUtils.isTouchingUnderBodyTextFrame(bodyTextFrame,
|
||||||
textBlock) && PositionUtils.getHeightDifferenceBetweenChunkWordAndDocumentWord(textBlock,
|
textBlock) && PositionUtils.getHeightDifferenceBetweenChunkWordAndDocumentWord(textBlock,
|
||||||
|
|||||||
@ -0,0 +1,75 @@
|
|||||||
|
package com.iqser.red.service.redaction.v1.server.classification.utils;
|
||||||
|
|
||||||
|
import java.awt.geom.Line2D;
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
|
|
||||||
|
import com.iqser.red.service.redaction.v1.model.Point;
|
||||||
|
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Ruling;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class CoordSystemHelper {
|
||||||
|
|
||||||
|
public Rectangle convertToDirAdj(Rectangle rectangle, float rotation, float pageWidth, float pageHeight) {
|
||||||
|
|
||||||
|
var topLeft = convertPoint(rectangle.getTopLeft().getX(), rectangle.getTopLeft().getY() + rectangle.getHeight(), rotation, pageWidth, pageHeight);
|
||||||
|
var bottomRight = convertPoint(rectangle.getTopLeft().getX() + rectangle.getWidth(), rectangle.getTopLeft().getY(), rotation, pageWidth, pageHeight);
|
||||||
|
|
||||||
|
return new Rectangle(new Point((float) topLeft.getX(), (float) topLeft.getY()),
|
||||||
|
(float) (bottomRight.getX() - topLeft.getX()),
|
||||||
|
(float) (bottomRight.getY() - topLeft.getY()),
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Line2D.Float convertToDirAdj(Ruling ruling, float rotation, float pageWidth, float pageHeight) {
|
||||||
|
|
||||||
|
return new Line2D.Float(convertPoint(ruling.x1, ruling.y1, rotation, pageWidth, pageHeight), convertPoint(ruling.x2, ruling.y2, rotation, pageWidth, pageHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Point2D convertPoint(float x, float y, float rotation, float pageWidth, float pageHeight) {
|
||||||
|
|
||||||
|
var xAdj = getXRot(x, y, rotation, pageWidth, pageHeight);
|
||||||
|
var yAdj = 0f;
|
||||||
|
if (rotation == 0 || rotation == 180) {
|
||||||
|
yAdj = pageHeight - getYLowerLeftRot(x, y, rotation, pageWidth, pageHeight);
|
||||||
|
} else {
|
||||||
|
yAdj = pageWidth - getYLowerLeftRot(x, y, rotation, pageWidth, pageHeight);
|
||||||
|
}
|
||||||
|
return new Point2D.Float(xAdj, yAdj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private float getXRot(float x, float y, float rotation, float pageWidth, float pageHeight) {
|
||||||
|
|
||||||
|
if (rotation == 0) {
|
||||||
|
return x;
|
||||||
|
} else if (rotation == 90) {
|
||||||
|
return y;
|
||||||
|
} else if (rotation == 180) {
|
||||||
|
return pageWidth - x;
|
||||||
|
} else if (rotation == 270) {
|
||||||
|
return pageHeight - y;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private float getYLowerLeftRot(float x, float y, float rotation, float pageWidth, float pageHeight) {
|
||||||
|
|
||||||
|
if (rotation == 0) {
|
||||||
|
return y;
|
||||||
|
} else if (rotation == 90) {
|
||||||
|
return pageWidth - x;
|
||||||
|
} else if (rotation == 180) {
|
||||||
|
return pageHeight - y;
|
||||||
|
} else if (rotation == 270) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
package com.iqser.red.service.redaction.v1.server.classification.utils;
|
package com.iqser.red.service.redaction.v1.server.classification.utils;
|
||||||
|
|
||||||
|
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Rectangle;
|
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
@ -19,7 +19,9 @@ public class PositionUtils {
|
|||||||
|
|
||||||
double threshold = textBlock.getMostPopularWordHeight() * 3;
|
double threshold = textBlock.getMostPopularWordHeight() * 3;
|
||||||
|
|
||||||
if (textBlock.getMinX() + threshold > btf.getX() && textBlock.getMaxX() - threshold < btf.getX() + btf.getWidth() && textBlock.getMinY() + threshold > btf.getY() && textBlock.getMaxY() - threshold < btf.getY() + btf.getHeight()) {
|
if (textBlock.getMinX() + threshold > btf.getTopLeft().getX() && textBlock.getMaxX() - threshold < btf.getTopLeft()
|
||||||
|
.getX() + btf.getWidth() && textBlock.getMinY() + threshold > btf.getTopLeft().getY() && textBlock.getMaxY() - threshold < btf.getTopLeft()
|
||||||
|
.getY() + btf.getHeight()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -28,16 +30,25 @@ public class PositionUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isOverBodyTextFrame(Rectangle btf, TextBlock textBlock, boolean rotated) {
|
public boolean isOverBodyTextFrame(Rectangle btf, TextBlock textBlock, int rotation) {
|
||||||
|
|
||||||
if (btf == null || textBlock == null) {
|
if (btf == null || textBlock == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rotated && textBlock.getMinX() < btf.getX()) {
|
if (rotation == 90 && textBlock.getX2() < btf.getTopLeft().getX()) {
|
||||||
// Its very strange, P{0,0} is on top left in this case, instead of lower left.
|
|
||||||
return true;
|
return true;
|
||||||
} else if (!rotated && textBlock.getMinY() > btf.getY() + btf.getHeight()) {
|
}
|
||||||
|
|
||||||
|
if (rotation == 180 && textBlock.getY2() < btf.getTopLeft().getY()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotation == 270 && textBlock.getX1() > btf.getTopLeft().getX() + btf.getWidth()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotation == 0 && textBlock.getY1() > btf.getTopLeft().getY() + btf.getHeight()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -46,15 +57,25 @@ public class PositionUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isUnderBodyTextFrame(Rectangle btf, TextBlock textBlock) {
|
public boolean isUnderBodyTextFrame(Rectangle btf, TextBlock textBlock, int rotation) {
|
||||||
|
|
||||||
//TODO Currently this is not working for rotated pages.
|
|
||||||
|
|
||||||
if (btf == null || textBlock == null) {
|
if (btf == null || textBlock == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (textBlock.getMaxY() < btf.getY()) {
|
if (rotation == 90 && textBlock.getX1() > btf.getTopLeft().getX() + btf.getWidth()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotation == 180 && textBlock.getY1() > btf.getTopLeft().getY() + btf.getHeight()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotation == 270 && textBlock.getX2() < btf.getTopLeft().getX()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotation == 0 && textBlock.getY2() < btf.getTopLeft().getY()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -71,7 +92,7 @@ public class PositionUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (textBlock.getMinY() < btf.getY()) {
|
if (textBlock.getMinY() < btf.getTopLeft().getY()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -147,30 +147,6 @@ public class TextPositionSequence implements CharSequence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
@JsonAttribute(ignore = true)
|
|
||||||
public float getX1() {
|
|
||||||
|
|
||||||
if (rotation == 90) {
|
|
||||||
return textPositions.get(0).getYDirAdj() - getTextHeight();
|
|
||||||
} else {
|
|
||||||
return textPositions.get(0).getXDirAdj();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
@JsonAttribute(ignore = true)
|
|
||||||
public float getX2() {
|
|
||||||
|
|
||||||
if (rotation == 90) {
|
|
||||||
return textPositions.get(0).getYDirAdj();
|
|
||||||
} else {
|
|
||||||
return textPositions.get(textPositions.size() - 1).getXDirAdj() + textPositions.get(textPositions.size() - 1).getWidthDirAdj() + HEIGHT_PADDING;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
@JsonAttribute(ignore = true)
|
@JsonAttribute(ignore = true)
|
||||||
public float getRotationAdjustedY() {
|
public float getRotationAdjustedY() {
|
||||||
@ -179,15 +155,30 @@ public class TextPositionSequence implements CharSequence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@JsonAttribute(ignore = true)
|
||||||
|
public float getX1() {
|
||||||
|
|
||||||
|
return textPositions.get(0).getXDirAdj();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@JsonAttribute(ignore = true)
|
||||||
|
public float getX2() {
|
||||||
|
|
||||||
|
return textPositions.get(textPositions.size() - 1).getXDirAdj() + textPositions.get(textPositions.size() - 1).getWidthDirAdj() + HEIGHT_PADDING;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
@JsonAttribute(ignore = true)
|
@JsonAttribute(ignore = true)
|
||||||
public float getY1() {
|
public float getY1() {
|
||||||
|
|
||||||
if (rotation == 90) {
|
return textPositions.get(0).getYDirAdj() - getTextHeight();
|
||||||
return textPositions.get(0).getXDirAdj();
|
|
||||||
} else {
|
|
||||||
return pageHeight - textPositions.get(0).getYDirAdj();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -195,11 +186,8 @@ public class TextPositionSequence implements CharSequence {
|
|||||||
@JsonAttribute(ignore = true)
|
@JsonAttribute(ignore = true)
|
||||||
public float getY2() {
|
public float getY2() {
|
||||||
|
|
||||||
if (rotation == 90) {
|
return textPositions.get(0).getYDirAdj();
|
||||||
return textPositions.get(textPositions.size() - 1).getXDirAdj() + getTextHeight() - HEIGHT_PADDING;
|
|
||||||
} else {
|
|
||||||
return pageHeight - textPositions.get(0).getYDirAdj() + getTextHeight();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -99,24 +99,27 @@ public class PdfSegmentationService {
|
|||||||
stripper.getText(pdDocument);
|
stripper.getText(pdDocument);
|
||||||
|
|
||||||
PDRectangle pdr = pdPage.getMediaBox();
|
PDRectangle pdr = pdPage.getMediaBox();
|
||||||
boolean isLandscape = pdr.getWidth() > pdr.getHeight();
|
|
||||||
|
|
||||||
int rotation = pdPage.getRotation();
|
int rotation = pdPage.getRotation();
|
||||||
boolean isRotated = rotation != 0 && rotation != 360;
|
boolean isLandscape = pdr.getWidth() > pdr.getHeight() && (rotation == 0 || rotation == 180) || pdr.getHeight() > pdr.getWidth() && (rotation == 90 || rotation == 270);
|
||||||
|
|
||||||
|
PDRectangle cropbox = pdPage.getCropBox();
|
||||||
CleanRulings cleanRulings = rulingCleaningService.getCleanRulings(pdfTableCells.get(pageNumber),
|
CleanRulings cleanRulings = rulingCleaningService.getCleanRulings(pdfTableCells.get(pageNumber),
|
||||||
stripper.getRulings(),
|
stripper.getRulings(),
|
||||||
stripper.getMinCharWidth(),
|
stripper.getMinCharWidth(),
|
||||||
stripper.getMaxCharHeight());
|
stripper.getMaxCharHeight());
|
||||||
Page page = blockificationService.blockify(stripper.getTextPositionSequences(), cleanRulings.getHorizontal(), cleanRulings.getVertical());
|
Page page = blockificationService.blockify(stripper.getTextPositionSequences(), cleanRulings.getHorizontal(), cleanRulings.getVertical(), rotation, cropbox);
|
||||||
|
|
||||||
PDRectangle cropbox = pdPage.getCropBox();
|
|
||||||
float cropboxArea = cropbox.getHeight() * cropbox.getWidth();
|
float cropboxArea = cropbox.getHeight() * cropbox.getWidth();
|
||||||
page.setCropBoxArea(cropboxArea);
|
page.setCropBoxArea(cropboxArea);
|
||||||
|
|
||||||
|
System.out.println("CropBox: w:" + cropbox.getWidth() + " h:" + cropbox.getHeight() + "MediaBox: w:" + pdPage.getMediaBox()
|
||||||
|
.getWidth() + " h:" + pdPage.getMediaBox().getHeight());
|
||||||
|
|
||||||
page.setRotation(rotation);
|
page.setRotation(rotation);
|
||||||
page.setLandscape(isLandscape || isRotated);
|
page.setLandscape(isLandscape);
|
||||||
page.setPageNumber(pageNumber);
|
page.setPageNumber(pageNumber);
|
||||||
|
page.setCropBox(cropbox);
|
||||||
|
|
||||||
tableExtractionService.extractTables(cleanRulings, page);
|
tableExtractionService.extractTables(cleanRulings, page);
|
||||||
buildPageStatistics(page);
|
buildPageStatistics(page);
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import com.dslplatform.json.JsonAttribute;
|
|||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.Orientation;
|
import com.iqser.red.service.redaction.v1.server.classification.model.Orientation;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -34,6 +35,12 @@ public abstract class AbstractTextContainer {
|
|||||||
public abstract String getText();
|
public abstract String getText();
|
||||||
|
|
||||||
|
|
||||||
|
public boolean containsBlock(TextBlock other) {
|
||||||
|
|
||||||
|
return this.minX <= other.getX1() && this.maxX >= other.getX2() && this.minY >= other.getY1() && this.maxY <= other.getY2();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean contains(AbstractTextContainer other) {
|
public boolean contains(AbstractTextContainer other) {
|
||||||
|
|
||||||
return this.minX <= other.minX && this.maxX >= other.maxX && this.minY >= other.minY && this.maxY <= other.maxY;
|
return this.minX <= other.minX && this.maxX >= other.maxX && this.minY >= other.minY && this.maxY <= other.maxY;
|
||||||
|
|||||||
@ -20,12 +20,11 @@ public class Rectangle extends Rectangle2D.Float {
|
|||||||
public static final Comparator<Rectangle> ILL_DEFINED_ORDER = new Comparator<Rectangle>() {
|
public static final Comparator<Rectangle> ILL_DEFINED_ORDER = new Comparator<Rectangle>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(Rectangle o1, Rectangle o2) {
|
public int compare(Rectangle o1, Rectangle o2) {
|
||||||
|
if (o1.equals(o2)) return 0;
|
||||||
if (o1.equals(o2)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (o1.verticalOverlap(o2) > VERTICAL_COMPARISON_THRESHOLD) {
|
if (o1.verticalOverlap(o2) > VERTICAL_COMPARISON_THRESHOLD) {
|
||||||
return o1.isLtrDominant() == -1 && o2.isLtrDominant() == -1 ? -java.lang.Double.compare(o1.getX(), o2.getX()) : java.lang.Double.compare(o1.getX(), o2.getX());
|
return o1.isLtrDominant() == -1 && o2.isLtrDominant() == -1
|
||||||
|
? -java.lang.Double.compare(o1.getX(), o2.getX())
|
||||||
|
: java.lang.Double.compare(o1.getX(), o2.getX());
|
||||||
} else {
|
} else {
|
||||||
return java.lang.Float.compare(o1.getBottom(), o2.getBottom());
|
return java.lang.Float.compare(o1.getBottom(), o2.getBottom());
|
||||||
}
|
}
|
||||||
@ -33,25 +32,23 @@ public class Rectangle extends Rectangle2D.Float {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public Rectangle() {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Rectangle() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Rectangle(float top, float left, float width, float height) {
|
public Rectangle(float top, float left, float width, float height) {
|
||||||
|
|
||||||
super();
|
super();
|
||||||
this.setRect(left, top, width, height);
|
this.setRect(left, top, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param rectangles
|
* @param rectangles
|
||||||
* @return minimum bounding box that contains all the rectangles
|
* @return minimum bounding box that contains all the rectangles
|
||||||
*/
|
*/
|
||||||
public static Rectangle boundingBoxOf(List<? extends Rectangle> rectangles) {
|
public static Rectangle boundingBoxOf(List<? extends Rectangle> rectangles) {
|
||||||
|
|
||||||
float minx = java.lang.Float.MAX_VALUE;
|
float minx = java.lang.Float.MAX_VALUE;
|
||||||
float miny = java.lang.Float.MAX_VALUE;
|
float miny = java.lang.Float.MAX_VALUE;
|
||||||
float maxx = java.lang.Float.MIN_VALUE;
|
float maxx = java.lang.Float.MIN_VALUE;
|
||||||
@ -66,62 +63,50 @@ public class Rectangle extends Rectangle2D.Float {
|
|||||||
return new Rectangle(miny, minx, maxx - minx, maxy - miny);
|
return new Rectangle(miny, minx, maxx - minx, maxy - miny);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int compareTo(Rectangle other) {
|
public int compareTo(Rectangle other) {
|
||||||
|
|
||||||
return ILL_DEFINED_ORDER.compare(this, other);
|
return ILL_DEFINED_ORDER.compare(this, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// I'm bad at Java and need this for fancy sorting in
|
// I'm bad at Java and need this for fancy sorting in
|
||||||
// technology.tabula.TextChunk.
|
// technology.tabula.TextChunk.
|
||||||
public int isLtrDominant() {
|
public int isLtrDominant() {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public float getArea() {
|
public float getArea() {
|
||||||
|
|
||||||
return this.width * this.height;
|
return this.width * this.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public float verticalOverlap(Rectangle other) {
|
public float verticalOverlap(Rectangle other) {
|
||||||
|
|
||||||
return Math.max(0, Math.min(this.getBottom(), other.getBottom()) - Math.max(this.getTop(), other.getTop()));
|
return Math.max(0, Math.min(this.getBottom(), other.getBottom()) - Math.max(this.getTop(), other.getTop()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean verticallyOverlaps(Rectangle other) {
|
public boolean verticallyOverlaps(Rectangle other) {
|
||||||
|
|
||||||
return verticalOverlap(other) > 0;
|
return verticalOverlap(other) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public float horizontalOverlap(Rectangle other) {
|
public float horizontalOverlap(Rectangle other) {
|
||||||
|
|
||||||
return Math.max(0, Math.min(this.getRight(), other.getRight()) - Math.max(this.getLeft(), other.getLeft()));
|
return Math.max(0, Math.min(this.getRight(), other.getRight()) - Math.max(this.getLeft(), other.getLeft()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean horizontallyOverlaps(Rectangle other) {
|
public boolean horizontallyOverlaps(Rectangle other) {
|
||||||
|
|
||||||
return horizontalOverlap(other) > 0;
|
return horizontalOverlap(other) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public float verticalOverlapRatio(Rectangle other) {
|
public float verticalOverlapRatio(Rectangle other) {
|
||||||
|
|
||||||
float rv = 0, delta = Math.min(this.getBottom() - this.getTop(), other.getBottom() - other.getTop());
|
float rv = 0, delta = Math.min(this.getBottom() - this.getTop(), other.getBottom() - other.getTop());
|
||||||
|
|
||||||
if (other.getTop() <= this.getTop() && this.getTop() <= other.getBottom() && other.getBottom() <= this.getBottom()) {
|
if (other.getTop() <= this.getTop() && this.getTop() <= other.getBottom()
|
||||||
|
&& other.getBottom() <= this.getBottom()) {
|
||||||
rv = (other.getBottom() - this.getTop()) / delta;
|
rv = (other.getBottom() - this.getTop()) / delta;
|
||||||
} else if (this.getTop() <= other.getTop() && other.getTop() <= this.getBottom() && this.getBottom() <= other.getBottom()) {
|
} else if (this.getTop() <= other.getTop() && other.getTop() <= this.getBottom()
|
||||||
|
&& this.getBottom() <= other.getBottom()) {
|
||||||
rv = (this.getBottom() - other.getTop()) / delta;
|
rv = (this.getBottom() - other.getTop()) / delta;
|
||||||
} else if (this.getTop() <= other.getTop() && other.getTop() <= other.getBottom() && other.getBottom() <= this.getBottom()) {
|
} else if (this.getTop() <= other.getTop() && other.getTop() <= other.getBottom()
|
||||||
|
&& other.getBottom() <= this.getBottom()) {
|
||||||
rv = (other.getBottom() - other.getTop()) / delta;
|
rv = (other.getBottom() - other.getTop()) / delta;
|
||||||
} else if (other.getTop() <= this.getTop() && this.getTop() <= this.getBottom() && this.getBottom() <= other.getBottom()) {
|
} else if (other.getTop() <= this.getTop() && this.getTop() <= this.getBottom()
|
||||||
|
&& this.getBottom() <= other.getBottom()) {
|
||||||
rv = (this.getBottom() - this.getTop()) / delta;
|
rv = (this.getBottom() - this.getTop()) / delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,85 +114,64 @@ public class Rectangle extends Rectangle2D.Float {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public float overlapRatio(Rectangle other) {
|
public float overlapRatio(Rectangle other) {
|
||||||
|
double intersectionWidth = Math.max(0,
|
||||||
double intersectionWidth = Math.max(0, Math.min(this.getRight(), other.getRight()) - Math.max(this.getLeft(), other.getLeft()));
|
Math.min(this.getRight(), other.getRight()) - Math.max(this.getLeft(), other.getLeft()));
|
||||||
double intersectionHeight = Math.max(0, Math.min(this.getBottom(), other.getBottom()) - Math.max(this.getTop(), other.getTop()));
|
double intersectionHeight = Math.max(0,
|
||||||
|
Math.min(this.getBottom(), other.getBottom()) - Math.max(this.getTop(), other.getTop()));
|
||||||
double intersectionArea = Math.max(0, intersectionWidth * intersectionHeight);
|
double intersectionArea = Math.max(0, intersectionWidth * intersectionHeight);
|
||||||
double unionArea = this.getArea() + other.getArea() - intersectionArea;
|
double unionArea = this.getArea() + other.getArea() - intersectionArea;
|
||||||
|
|
||||||
return (float) (intersectionArea / unionArea);
|
return (float) (intersectionArea / unionArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Rectangle merge(Rectangle other) {
|
public Rectangle merge(Rectangle other) {
|
||||||
|
|
||||||
this.setRect(this.createUnion(other));
|
this.setRect(this.createUnion(other));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public float getTop() {
|
public float getTop() {
|
||||||
|
|
||||||
return (float) this.getMinY();
|
return (float) this.getMinY();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setTop(float top) {
|
public void setTop(float top) {
|
||||||
|
|
||||||
float deltaHeight = top - this.y;
|
float deltaHeight = top - this.y;
|
||||||
this.setRect(this.x, top, this.width, this.height - deltaHeight);
|
this.setRect(this.x, top, this.width, this.height - deltaHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public float getRight() {
|
public float getRight() {
|
||||||
|
|
||||||
return (float) this.getMaxX();
|
return (float) this.getMaxX();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setRight(float right) {
|
public void setRight(float right) {
|
||||||
|
|
||||||
this.setRect(this.x, this.y, right - this.x, this.height);
|
this.setRect(this.x, this.y, right - this.x, this.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public float getLeft() {
|
public float getLeft() {
|
||||||
|
|
||||||
return (float) this.getMinX();
|
return (float) this.getMinX();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setLeft(float left) {
|
public void setLeft(float left) {
|
||||||
|
|
||||||
float deltaWidth = left - this.x;
|
float deltaWidth = left - this.x;
|
||||||
this.setRect(left, this.y, this.width - deltaWidth, this.height);
|
this.setRect(left, this.y, this.width - deltaWidth, this.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public float getBottom() {
|
public float getBottom() {
|
||||||
|
|
||||||
return (float) this.getMaxY();
|
return (float) this.getMaxY();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setBottom(float bottom) {
|
public void setBottom(float bottom) {
|
||||||
|
|
||||||
this.setRect(this.x, this.y, this.width, bottom - this.y);
|
this.setRect(this.x, this.y, this.width, bottom - this.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Point2D[] getPoints() {
|
public Point2D[] getPoints() {
|
||||||
|
return new Point2D[]{new Point2D.Float(this.getLeft(), this.getTop()),
|
||||||
return new Point2D[]{new Point2D.Float(this.getLeft(), this.getTop()), new Point2D.Float(this.getRight(), this.getTop()), new Point2D.Float(this.getRight(),
|
new Point2D.Float(this.getRight(), this.getTop()), new Point2D.Float(this.getRight(), this.getBottom()),
|
||||||
this.getBottom()), new Point2D.Float(this.getLeft(), this.getBottom())};
|
new Point2D.Float(this.getLeft(), this.getBottom())};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
String s = super.toString();
|
String s = super.toString();
|
||||||
sb.append(s.substring(0, s.length() - 1));
|
sb.append(s.substring(0, s.length() - 1));
|
||||||
|
|||||||
@ -1,15 +1,27 @@
|
|||||||
package com.iqser.red.service.redaction.v1.server.tableextraction.service;
|
package com.iqser.red.service.redaction.v1.server.tableextraction.service;
|
||||||
|
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.Page;
|
import java.awt.geom.Point2D;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
import java.util.ArrayList;
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.*;
|
import java.util.Comparator;
|
||||||
import com.iqser.red.service.redaction.v1.server.tableextraction.utils.Utils;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.awt.geom.Point2D;
|
import com.iqser.red.service.redaction.v1.server.classification.model.Page;
|
||||||
import java.util.*;
|
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
||||||
import java.util.stream.Collectors;
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Cell;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.CleanRulings;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Rectangle;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Ruling;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Table;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.tableextraction.utils.Utils;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class TableExtractionService {
|
public class TableExtractionService {
|
||||||
@ -63,7 +75,7 @@ public class TableExtractionService {
|
|||||||
for (AbstractTextContainer abstractTextContainer : page.getTextBlocks()) {
|
for (AbstractTextContainer abstractTextContainer : page.getTextBlocks()) {
|
||||||
TextBlock textBlock = (TextBlock) abstractTextContainer;
|
TextBlock textBlock = (TextBlock) abstractTextContainer;
|
||||||
for (Cell cell : cells) {
|
for (Cell cell : cells) {
|
||||||
if (cell.intersects(textBlock.getMinX(), textBlock.getMinY(), textBlock.getWidth(), textBlock.getHeight())) {
|
if (cell.intersects(textBlock.getX1(), textBlock.getY1(), textBlock.getX2() - textBlock.getX1(), textBlock.getY2() - textBlock.getY1())) {
|
||||||
cell.addTextBlock(textBlock);
|
cell.addTextBlock(textBlock);
|
||||||
toBeRemoved.add(textBlock);
|
toBeRemoved.add(textBlock);
|
||||||
break;
|
break;
|
||||||
@ -94,7 +106,7 @@ public class TableExtractionService {
|
|||||||
Iterator<AbstractTextContainer> itty = page.getTextBlocks().iterator();
|
Iterator<AbstractTextContainer> itty = page.getTextBlocks().iterator();
|
||||||
while (itty.hasNext()) {
|
while (itty.hasNext()) {
|
||||||
AbstractTextContainer textBlock = itty.next();
|
AbstractTextContainer textBlock = itty.next();
|
||||||
if (table.contains(textBlock) && position == -1) {
|
if (textBlock instanceof TextBlock ? table.containsBlock((TextBlock) textBlock) : table.contains(textBlock) && position == -1) {
|
||||||
position = page.getTextBlocks().indexOf(textBlock);
|
position = page.getTextBlocks().indexOf(textBlock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,15 @@
|
|||||||
package com.iqser.red.service.redaction.v1.server.visualization.service;
|
package com.iqser.red.service.redaction.v1.server.visualization.service;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.Document;
|
import com.iqser.red.service.redaction.v1.server.classification.model.Document;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.Page;
|
import com.iqser.red.service.redaction.v1.server.classification.model.Page;
|
||||||
import com.iqser.red.service.redaction.v1.server.classification.model.Paragraph;
|
import com.iqser.red.service.redaction.v1.server.classification.model.Paragraph;
|
||||||
@ -11,16 +21,6 @@ import com.iqser.red.service.redaction.v1.server.tableextraction.model.Table;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
|
||||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@ -79,10 +79,16 @@ public class PdfVisualisationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contentStream.setStrokingColor(Color.YELLOW);
|
contentStream.setStrokingColor(Color.YELLOW);
|
||||||
contentStream.addRect((float) analyzedPage.getBodyTextFrame().getX(),
|
// var width = analyzedPage.getBodyTextFrame().getX2(pdPage.getRotation(), pdPage.getCropBox().getWidth(), pdPage.getCropBox().getHeight()) - analyzedPage.getBodyTextFrame().getX1(pdPage.getRotation(), pdPage.getCropBox().getWidth(), pdPage.getCropBox().getHeight());
|
||||||
(float) analyzedPage.getBodyTextFrame().getY(),
|
// var height = analyzedPage.getBodyTextFrame().getY2(pdPage.getRotation(), pdPage.getCropBox().getWidth(), pdPage.getCropBox().getHeight()) - analyzedPage.getBodyTextFrame().getY1(pdPage.getRotation(), pdPage.getCropBox().getWidth(), pdPage.getCropBox().getHeight());
|
||||||
(float) analyzedPage.getBodyTextFrame().getWidth(),
|
//
|
||||||
(float) analyzedPage.getBodyTextFrame().getHeight());
|
// contentStream.addRect(analyzedPage.getBodyTextFrame().getX1(pdPage.getRotation(), pdPage.getCropBox().getWidth(), pdPage.getCropBox().getHeight()), analyzedPage.getBodyTextFrame().getY1(pdPage.getRotation(), pdPage.getCropBox().getWidth(), pdPage.getCropBox().getHeight()), width, height);
|
||||||
|
|
||||||
|
contentStream.addRect(analyzedPage.getBodyTextFrame().getTopLeft().getX(),
|
||||||
|
analyzedPage.getBodyTextFrame().getTopLeft().getY(),
|
||||||
|
analyzedPage.getBodyTextFrame().getWidth(),
|
||||||
|
analyzedPage.getBodyTextFrame().getHeight());
|
||||||
|
|
||||||
contentStream.stroke();
|
contentStream.stroke();
|
||||||
|
|
||||||
contentStream.close();
|
contentStream.close();
|
||||||
@ -94,20 +100,43 @@ public class PdfVisualisationService {
|
|||||||
|
|
||||||
contentStream.setStrokingColor(Color.RED);
|
contentStream.setStrokingColor(Color.RED);
|
||||||
|
|
||||||
contentStream.addRect(textBlock.getMinX(), textBlock.getMinY(), textBlock.getWidth(), textBlock.getHeight());
|
contentStream.addRect(textBlock.getX1(), textBlock.getY1(), textBlock.getX2() - textBlock.getX1(), textBlock.getY2() - textBlock.getY1());
|
||||||
contentStream.stroke();
|
contentStream.stroke();
|
||||||
|
|
||||||
if (textBlock.getClassification() != null) {
|
if (textBlock.getClassification() != null) {
|
||||||
contentStream.beginText();
|
contentStream.beginText();
|
||||||
|
|
||||||
contentStream.setNonStrokingColor(Color.BLUE);
|
contentStream.setNonStrokingColor(Color.BLUE);
|
||||||
contentStream.setFont(PDType1Font.TIMES_ROMAN, 12f);
|
contentStream.setFont(PDType1Font.TIMES_ROMAN, 9f);
|
||||||
|
|
||||||
contentStream.newLineAtOffset(textBlock.getMinX(), textBlock.getMaxY());
|
contentStream.newLineAtOffset(textBlock.getX1(), textBlock.getY2() + 2);
|
||||||
|
contentStream.showText(textBlock.getClassification() + textBlock.getOrientation() + "-->" + textBlock.getSequences().get(0).getDir());
|
||||||
contentStream.showText(textBlock.getClassification() + textBlock.getOrientation());
|
|
||||||
|
|
||||||
contentStream.endText();
|
contentStream.endText();
|
||||||
|
|
||||||
|
// contentStream.setNonStrokingColor(Color.BLUE);
|
||||||
|
// contentStream.setFont(PDType1Font.TIMES_ROMAN, 2f);
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// contentStream.beginText();
|
||||||
|
// contentStream.newLineAtOffset(textBlock.getX1(), textBlock.getY1());
|
||||||
|
// contentStream.showText("MinX,MinY(" + textBlock.getX1() + "," + textBlock.getY1() + ")");
|
||||||
|
// contentStream.endText();
|
||||||
|
// contentStream.beginText();
|
||||||
|
// contentStream.newLineAtOffset(textBlock.getX2(), textBlock.getY1());
|
||||||
|
// contentStream.showText("MaxX,MinY(" + textBlock.getX2() + "," + textBlock.getY1() + ")");
|
||||||
|
// contentStream.endText();
|
||||||
|
// contentStream.beginText();
|
||||||
|
// contentStream.newLineAtOffset(textBlock.getX1(), textBlock.getY2());
|
||||||
|
// contentStream.showText("MinX,MaxY(" + textBlock.getX1() + "," + textBlock.getY2() + ")");
|
||||||
|
// contentStream.endText();
|
||||||
|
// contentStream.beginText();
|
||||||
|
// contentStream.newLineAtOffset(textBlock.getX2(), textBlock.getY2());
|
||||||
|
// contentStream.showText("MaxX,MaxY(" + textBlock.getX2() + "," + textBlock.getY2() + ")");
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// contentStream.endText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +153,7 @@ public class PdfVisualisationService {
|
|||||||
|
|
||||||
contentStream.setStrokingColor(Color.GREEN);
|
contentStream.setStrokingColor(Color.GREEN);
|
||||||
for (TextBlock textBlock : cell.getTextBlocks()) {
|
for (TextBlock textBlock : cell.getTextBlocks()) {
|
||||||
contentStream.addRect(textBlock.getMinX(), textBlock.getMinY(), textBlock.getWidth(), textBlock.getHeight());
|
contentStream.addRect(textBlock.getX1(), textBlock.getY1(), textBlock.getX2() - textBlock.getX1(), textBlock.getY2() - textBlock.getY1());
|
||||||
contentStream.stroke();
|
contentStream.stroke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1098,7 +1098,7 @@ public class RedactionIntegrationTest {
|
|||||||
|
|
||||||
System.out.println("classificationTest");
|
System.out.println("classificationTest");
|
||||||
|
|
||||||
AnalyzeRequest request = prepareStorage("files/Metolachlor/S-Metolachlor_RAR_01_Volume_1_2018-09-06.pdf");
|
AnalyzeRequest request = prepareStorage("files/new/VV-511309_OCR.pdf");
|
||||||
|
|
||||||
RedactionRequest redactionRequest = RedactionRequest.builder()
|
RedactionRequest redactionRequest = RedactionRequest.builder()
|
||||||
.dossierId(request.getDossierId())
|
.dossierId(request.getDossierId())
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user