add colorspaces to documetn analysis, improve barcode analysis

This commit is contained in:
Jörg Prante 2023-12-07 13:47:58 +01:00
parent 734cdfc90c
commit 7b624e8f30
5 changed files with 141 additions and 120 deletions

View file

@ -1,5 +1,5 @@
group = org.xbib.graphics group = org.xbib.graphics
name = graphics name = graphics
version = 5.2.0 version = 5.2.1
org.gradle.warning.mode = ALL org.gradle.warning.mode = ALL

View file

@ -28,68 +28,73 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
public class BarcodeAnalyzer { public class BarcodeAnalyzer {
private static final Logger logger = Logger.getLogger(BarcodeAnalyzer.class.getName()); private final BarcodeAnalyzerResult result;
private final List<Result> resultList;
private final Set<COSStream> seen; private final Set<COSStream> seen;
private float saturationMin;
private float brightnessMin;
private int maximumBlankPixelDelimiterCount;
public BarcodeAnalyzer() { public BarcodeAnalyzer() {
this.resultList = new ArrayList<>(); this.result = new BarcodeAnalyzerResult();
this.seen = new HashSet<>(); this.seen = new HashSet<>();
this.saturationMin = 0.10f;
this.brightnessMin = 0.80f;
this.maximumBlankPixelDelimiterCount = 20;
}
public void setSaturationMin(float saturationMin) {
this.saturationMin = saturationMin;
}
public void setBrightnessMin(float brightnessMin) {
this.brightnessMin = brightnessMin;
}
public void setMaximumBlankPixelDelimiterCount(int maximumBlankPixelDelimiterCount) {
this.maximumBlankPixelDelimiterCount = maximumBlankPixelDelimiterCount;
} }
public void process(File file, int pageNumber) public void process(File file, int pageNumber)
throws IOException { throws IOException {
try (PDDocument document = Loader.loadPDF(file)) { try (PDDocument document = Loader.loadPDF(file)) {
process(document.getPage(pageNumber), 20); if (pageNumber >= 0 && pageNumber < document.getNumberOfPages()) {
process(document.getPage(pageNumber));
}
} }
} }
public void process(File file, int pageNumber, int maximumBlankPixelDelimiterCount) public void process(PDPage pdPage) throws IOException {
throws IOException { Objects.requireNonNull(pdPage);
try (PDDocument document = Loader.loadPDF(file)) {
process(document.getPage(pageNumber), maximumBlankPixelDelimiterCount);
}
}
public void process(PDPage pdPage, int maximumBlankPixelDelimiterCount)
throws IOException {
PDResources pdResources = pdPage.getResources(); PDResources pdResources = pdPage.getResources();
for (COSName name : pdResources.getXObjectNames()) { for (COSName name : pdResources.getXObjectNames()) {
PDXObject xobject = pdResources.getXObject(name); PDXObject xobject = pdResources.getXObject(name);
seen.add(xobject.getCOSObject()); seen.add(xobject.getCOSObject());
if (xobject instanceof PDImageXObject imageXObject) { if (xobject instanceof PDImageXObject imageXObject) {
BufferedImage image = imageXObject.getImage(); extractBarcode(imageXObject.getImage());
extractBarcodeArrayByAreas(image, maximumBlankPixelDelimiterCount);
} }
} }
PageExtractor pageExtractor = new PageExtractor(pdPage, seen); PageExtractor pageExtractor = new PageExtractor(pdPage, seen);
pageExtractor.process(); pageExtractor.process();
for (PDImageXObject imageXObject : pageExtractor.getImages()) { for (PDImageXObject imageXObject : pageExtractor.getImages()) {
BufferedImage image = imageXObject.getImage(); extractBarcode(imageXObject.getImage());
extractBarcodeArrayByAreas(image, maximumBlankPixelDelimiterCount);
} }
} }
public List<Result> getResultList() { public List<Map.Entry<String, String>> getResult() {
return resultList; return result.getResult();
} }
private void extractBarcodeArrayByAreas(BufferedImage image, int maximumBlankPixelDelimiterCount) { private void extractBarcode(BufferedImage image) {
float saturationMin = 0.10f; for (Rectangle rectangle : getAreas(computeBlackAndWhite(image))) {
float brightnessMin = 0.80f;
BufferedImage blackAndWhiteImage = getThresholdImage(image, saturationMin, brightnessMin);
List<Rectangle> areaList = getAllAreaByColor(blackAndWhiteImage, null, 0, 0, 0,
maximumBlankPixelDelimiterCount);
logger.log(Level.FINE, "found " + areaList + " rectangles");
for (Rectangle rectangle : areaList) {
if (rectangle.x < 0) { if (rectangle.x < 0) {
rectangle.x = 0; rectangle.x = 0;
} }
@ -103,51 +108,32 @@ public class BarcodeAnalyzer {
rectangle.width = image.getWidth() - rectangle.x; rectangle.width = image.getWidth() - rectangle.x;
} }
BufferedImage croppedImage = image.getSubimage(rectangle.x, rectangle.y, rectangle.width, rectangle.height); BufferedImage croppedImage = image.getSubimage(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
Result result = decodeBarcode(croppedImage); LuminanceSource source = new BufferedImageLuminanceSource(croppedImage);
if (result != null) { BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
resultList.add(result); Reader reader = new MultiFormatReader();
try {
Result result = reader.decode(bitmap);
if (result != null) {
this.result.add(Map.entry(result.getBarcodeFormat().name(), result.getText()));
}
} catch (NotFoundException | ChecksumException | FormatException e) {
// ignore 'not found' or other problems
} }
} }
} }
private BufferedImage getThresholdImage(BufferedImage image, float saturationMin, float brightnessMin) { private List<Rectangle> getAreas(BufferedImage bufferedImage) {
BufferedImage result = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); int redColor = 0;
Graphics2D graphics2D = result.createGraphics(); int greenColor = 0;
graphics2D.drawRenderedImage(image, null); int blueColor = 0;
graphics2D.dispose(); int w = bufferedImage.getWidth();
computeBlackAndWhite(image, result, saturationMin, brightnessMin); int h = bufferedImage.getHeight();
return result;
}
private Result decodeBarcode(BufferedImage bufferedImage) {
LuminanceSource source = new BufferedImageLuminanceSource(bufferedImage);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Reader reader = new MultiFormatReader();
try {
return reader.decode(bitmap);
} catch (NotFoundException | ChecksumException | FormatException e) {
// ignore 'not found' or other problems
}
return null;
}
private List<Rectangle> getAllAreaByColor(BufferedImage in, BufferedImage out,
int redColor, int greenColor, int blueColor,
int maximumBlankPixelDelimiterCount) {
int w = in.getWidth();
int h = in.getHeight();
int pixel; int pixel;
List<Rectangle> areaList = new ArrayList<>(); List<Rectangle> areaList = new ArrayList<>();
Graphics2D gc = null;
if (out != null) {
gc = out.createGraphics();
gc.setColor(new Color(1f, 0f, 0f));
}
int maximumBlankPixelDelimiterCountDouble = maximumBlankPixelDelimiterCount * 2; int maximumBlankPixelDelimiterCountDouble = maximumBlankPixelDelimiterCount * 2;
for (int x = 0; x < w; x++) { for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) { for (int y = 0; y < h; y++) {
pixel = in.getRGB(x, y); pixel = bufferedImage.getRGB(x, y);
int alpha = ((pixel >> 24) & 0xFF);
int red = ((pixel >> 16) & 0xFF); int red = ((pixel >> 16) & 0xFF);
int green = ((pixel >> 8) & 0xFF); int green = ((pixel >> 8) & 0xFF);
int blue = (pixel & 0xFF); int blue = (pixel & 0xFF);
@ -167,12 +153,6 @@ public class BarcodeAnalyzer {
if (isInArea) { if (isInArea) {
continue; continue;
} }
pixel = 0;
pixel = pixel | (alpha << 24);
pixel = pixel | (0);
pixel = pixel | (255 << 8);
pixel = pixel | (0);
isInArea = false;
for (Rectangle rectangle : areaList) { for (Rectangle rectangle : areaList) {
Rectangle intersection = rectangle.intersection(rect); Rectangle intersection = rectangle.intersection(rect);
if (intersection.width > 0 && intersection.height > 0) { if (intersection.width > 0 && intersection.height > 0) {
@ -206,24 +186,25 @@ public class BarcodeAnalyzer {
areaList.remove(rectToRemove); areaList.remove(rectToRemove);
} }
} }
if (out != null) {
out.setRGB(x, y, pixel);
gc.draw(rect);
}
} }
} }
} }
return areaList; return areaList;
} }
private void computeBlackAndWhite(BufferedImage in, BufferedImage out, float saturationMin, float brightnessMin) { private BufferedImage computeBlackAndWhite(BufferedImage bufferedImage) {
int w = in.getWidth(); BufferedImage outputBufferedImage =
int h = in.getHeight(); new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics2D = outputBufferedImage.createGraphics();
graphics2D.drawRenderedImage(bufferedImage, null);
graphics2D.dispose();
int w = bufferedImage.getWidth();
int h = bufferedImage.getHeight();
int pixel; int pixel;
float[] hsb = new float[3]; float[] hsb = new float[3];
for (int x = 0; x < w; x++) { for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) { for (int y = 0; y < h; y++) {
pixel = in.getRGB(x, y); pixel = bufferedImage.getRGB(x, y);
int alpha = ((pixel >> 24) & 0xFF); int alpha = ((pixel >> 24) & 0xFF);
int red = ((pixel >> 16) & 0xFF); int red = ((pixel >> 16) & 0xFF);
int green = ((pixel >> 8) & 0xFF); int green = ((pixel >> 8) & 0xFF);
@ -243,9 +224,27 @@ public class BarcodeAnalyzer {
pixel = pixel | (red << 16); pixel = pixel | (red << 16);
pixel = pixel | (green << 8); pixel = pixel | (green << 8);
pixel = pixel | (blue); pixel = pixel | (blue);
out.setRGB(x, y, pixel); outputBufferedImage.setRGB(x, y, pixel);
} }
} }
return outputBufferedImage;
}
public static class BarcodeAnalyzerResult {
private final List<Map.Entry<String, String>> result;
private BarcodeAnalyzerResult() {
this.result = new ArrayList<>();
}
public List<Map.Entry<String, String>> getResult() {
return result;
}
private void add(Map.Entry<String, String> entry) {
result.add(entry);
}
} }
private static class PageExtractor extends PDFGraphicsStreamEngine { private static class PageExtractor extends PDFGraphicsStreamEngine {

View file

@ -18,6 +18,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@ -44,6 +45,7 @@ public class DocumentAnalyzer {
try (PDDocument document = Loader.loadPDF(file)) { try (PDDocument document = Loader.loadPDF(file)) {
documentInformationToResult(document.getDocumentInformation()); documentInformationToResult(document.getDocumentInformation());
List<Map<String, Object>> pages = new ArrayList<>(); List<Map<String, Object>> pages = new ArrayList<>();
Collection<String> colorspaces = new LinkedHashSet<>();
int documentimagecount = 0; int documentimagecount = 0;
int pagecount = document.getNumberOfPages(); int pagecount = document.getNumberOfPages();
boolean isDocumentColor = false; boolean isDocumentColor = false;
@ -55,6 +57,7 @@ public class DocumentAnalyzer {
for (int i = 0; i < pagecount; i++) { for (int i = 0; i < pagecount; i++) {
PDPage pdPage = document.getPage(i); PDPage pdPage = document.getPage(i);
Map<String, Object> pageMap = analyzePage(i, pdPage, seen); Map<String, Object> pageMap = analyzePage(i, pdPage, seen);
colorspaces.addAll((Collection<String>) pageMap.get("colorspaces"));
boolean isColor = (boolean) pageMap.get("iscolor"); boolean isColor = (boolean) pageMap.get("iscolor");
if (isColor) { if (isColor) {
isDocumentColor = true; isDocumentColor = true;
@ -79,6 +82,7 @@ public class DocumentAnalyzer {
} }
result.put("pagecount", pagecount); result.put("pagecount", pagecount);
result.put("imagecount", documentimagecount); result.put("imagecount", documentimagecount);
result.put("colorspaces", colorspaces);
result.put("isimage", pagecount > 0 && isDocumentImage); result.put("isimage", pagecount > 0 && isDocumentImage);
result.put("iscolor", isDocumentColor); result.put("iscolor", isDocumentColor);
result.put("isgray", isDocumentGray); result.put("isgray", isDocumentGray);
@ -103,6 +107,11 @@ public class DocumentAnalyzer {
return (int) result.get("pagecount"); return (int) result.get("pagecount");
} }
@SuppressWarnings("unchecked")
public Collection<String> getColorSpaces() {
return (Collection<String>) result.get("colorspaces");
}
public boolean isColor() { public boolean isColor() {
return (boolean) result.get("iscolor"); return (boolean) result.get("iscolor");
} }
@ -173,11 +182,18 @@ public class DocumentAnalyzer {
m.put("cropbox", Map.of("height", page.getCropBox().getHeight(), "width", page.getCropBox().getWidth())); m.put("cropbox", Map.of("height", page.getCropBox().getHeight(), "width", page.getCropBox().getWidth()));
m.put("mediabox", Map.of("height", page.getMediaBox().getHeight(), "width", page.getMediaBox().getWidth())); m.put("mediabox", Map.of("height", page.getMediaBox().getHeight(), "width", page.getMediaBox().getWidth()));
m.put("bleedbox", Map.of("height", page.getBleedBox().getHeight(), "width", page.getBleedBox().getWidth())); m.put("bleedbox", Map.of("height", page.getBleedBox().getHeight(), "width", page.getBleedBox().getWidth()));
if (page.getTrimBox() != null) {
m.put("trimbox", Map.of("height", page.getTrimBox().getHeight(), "width", page.getTrimBox().getWidth()));
}
if (page.getArtBox() != null) {
m.put("artbox", Map.of("height", page.getArtBox().getHeight(), "width", page.getArtBox().getWidth()));
}
checkBoxDimensions(page, m); checkBoxDimensions(page, m);
m.put("rotation", page.getRotation()); m.put("rotation", page.getRotation());
PageExtractor pageExtractor = new PageExtractor(page, seen); PageExtractor pageExtractor = new PageExtractor(page, seen);
pageExtractor.process(); pageExtractor.process();
m.put("images", pageExtractor.getImages()); m.put("images", pageExtractor.getImages());
m.put("colorspaces", pageExtractor.getColorSpaces());
m.put("iscolor", pageExtractor.isColor()); m.put("iscolor", pageExtractor.isColor());
m.put("isgray", pageExtractor.isGray()); m.put("isgray", pageExtractor.isGray());
FontExtractor fontExtractor = new FontExtractor(page); FontExtractor fontExtractor = new FontExtractor(page);
@ -190,10 +206,17 @@ public class DocumentAnalyzer {
List<Boolean> isA4 = new ArrayList<>(); List<Boolean> isA4 = new ArrayList<>();
List<Boolean> isLetter = new ArrayList<>(); List<Boolean> isLetter = new ArrayList<>();
List<Boolean> isLandscape= new ArrayList<>(); List<Boolean> isLandscape= new ArrayList<>();
List.of(Map.entry("bbox", page.getBBox()), List<Map.Entry<String, PDRectangle>> boxes = new ArrayList<>(List.of(Map.entry("bbox", page.getBBox()),
Map.entry("cropbox", page.getCropBox()), Map.entry("cropbox", page.getCropBox()),
Map.entry("mediabox", page.getMediaBox()), Map.entry("mediabox", page.getMediaBox()),
Map.entry("bleedbox", page.getBleedBox())).forEach(e -> { Map.entry("bleedbox", page.getBleedBox())));
if (page.getTrimBox() != null) {
boxes.add(Map.entry("trimbox", page.getTrimBox()));
}
if (page.getArtBox() != null) {
boxes.add(Map.entry("artbox", page.getArtBox()));
}
boxes.forEach(e -> {
String boxName = e.getKey(); String boxName = e.getKey();
PDRectangle rect = e.getValue(); PDRectangle rect = e.getValue();
Set<String> set = new LinkedHashSet<>(); Set<String> set = new LinkedHashSet<>();
@ -392,16 +415,19 @@ public class DocumentAnalyzer {
private final Set<COSStream> seen; private final Set<COSStream> seen;
private boolean isColor; private final Collection<String> colorSpaces;
private boolean isGray; private boolean isGray;
private boolean isBlackWhite;
private PageExtractor(PDPage page, Set<COSStream> seen) { private PageExtractor(PDPage page, Set<COSStream> seen) {
super(page); super(page);
this.seen = seen; this.seen = seen;
this.images = new ArrayList<>(); this.images = new ArrayList<>();
this.isColor = false; this.colorSpaces = new LinkedHashSet<>();
this.isGray = false; this.isGray = false;
this.isBlackWhite = false;
} }
public void process() throws IOException { public void process() throws IOException {
@ -412,14 +438,22 @@ public class DocumentAnalyzer {
return images; return images;
} }
public Collection<String> getColorSpaces() {
return colorSpaces;
}
public boolean isColor() { public boolean isColor() {
return isColor; return isColor(colorSpaces);
} }
public boolean isGray() { public boolean isGray() {
return isGray; return isGray;
} }
public boolean isBlackWhite() {
return isBlackWhite;
}
@Override @Override
public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) { public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) {
} }
@ -434,14 +468,15 @@ public class DocumentAnalyzer {
seen.add(xobject.getCOSObject()); seen.add(xobject.getCOSObject());
Map<String, Object> m = new LinkedHashMap<>(); Map<String, Object> m = new LinkedHashMap<>();
String colorSpaceName = xobject.getColorSpace().getName(); String colorSpaceName = xobject.getColorSpace().getName();
boolean isColorSpace = isColorSpaceName(colorSpaceName); colorSpaces.add(colorSpaceName);
if (isColorSpace) { if (isColor(List.of(colorSpaceName))) {
this.isColor = true; m.put("iscolor", true);
} }
boolean isGraySpace = isGraySpaceName(colorSpaceName); if ("DeviceGray".equals(colorSpaceName)) {
if (isGraySpace) { // black & white check
if (xobject.getBitsPerComponent() > 1) { if (xobject.getBitsPerComponent() > 1) {
this.isGray = true; this.isGray = true;
m.put("isgray", true);
} }
} }
m.put("width", xobject.getWidth()); m.put("width", xobject.getWidth());
@ -449,8 +484,6 @@ public class DocumentAnalyzer {
m.put("bitspercomponent", xobject.getBitsPerComponent()); m.put("bitspercomponent", xobject.getBitsPerComponent());
m.put("colorspace", colorSpaceName); m.put("colorspace", colorSpaceName);
m.put("suffix", xobject.getSuffix()); m.put("suffix", xobject.getSuffix());
m.put("iscolor", isColorSpace);
m.put("isgray", isGraySpace);
images.add(m); images.add(m);
} }
} }
@ -486,24 +519,12 @@ public class DocumentAnalyzer {
@Override @Override
public void strokePath() { public void strokePath() {
String colorSpaceName = getGraphicsState().getStrokingColor().getColorSpace().getName(); colorSpaces.add(getGraphicsState().getStrokingColor().getColorSpace().getName());
if (isColorSpaceName(colorSpaceName)) {
this.isColor = true;
}
if (isGraySpaceName(colorSpaceName)) {
this.isGray = true;
}
} }
@Override @Override
public void fillPath(int windingRule) { public void fillPath(int windingRule) {
String colorSpaceName = getGraphicsState().getStrokingColor().getColorSpace().getName(); colorSpaces.add(getGraphicsState().getStrokingColor().getColorSpace().getName());
if (isColorSpaceName(colorSpaceName)) {
this.isColor = true;
}
if (isGraySpaceName(colorSpaceName)) {
this.isGray = true;
}
} }
@Override @Override
@ -514,15 +535,15 @@ public class DocumentAnalyzer {
public void shadingFill(COSName shadingName) { public void shadingFill(COSName shadingName) {
} }
private boolean isColorSpaceName(String name) { private boolean isColor(Collection<String> colorSpaceNames) {
return "DeviceRGB".equals(name) || return colorSpaceNames.contains("DeviceRGB") ||
"DeviceCMYK".equals(name) || colorSpaceNames.contains("DeviceCYMK") ||
"ICCBased".equals(name) || colorSpaceNames.contains("ICCBased") ||
"Indexed".equals(name); colorSpaceNames.contains("Indexed");
} }
private boolean isGraySpaceName(String name) { private boolean isGray(Collection<String> colorSpaceNames) {
return "DeviceGray".equals(name); return colorSpaceNames.contains("DeviceGray");
} }
} }

View file

@ -2,13 +2,13 @@ package org.xbib.graphics.pdfbox.test;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.xbib.graphics.pdfbox.analyze.BarcodeAnalyzer; import org.xbib.graphics.pdfbox.analyze.BarcodeAnalyzer;
import org.xbib.graphics.zxing.Result;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -19,16 +19,16 @@ public class BarcodeAnalyzerTest {
@Test @Test
public void testBarcodeAnalysis() throws IOException { public void testBarcodeAnalysis() throws IOException {
Path tmp = Files.createTempDirectory("barcode-analyzer"); Path tmp = Files.createTempDirectory("barcode-analyzer");
String sample = "394_394-F3GIS2JO.pdf"; String sample = "test.pdf";
Path path = tmp.resolve(sample); Path path = tmp.resolve(sample);
try (InputStream inputStream = getClass().getResourceAsStream(sample); try (InputStream inputStream = getClass().getResourceAsStream(sample);
OutputStream outputStream = Files.newOutputStream(path)) { OutputStream outputStream = Files.newOutputStream(path)) {
if (inputStream != null) { if (inputStream != null) {
inputStream.transferTo(outputStream); inputStream.transferTo(outputStream);
BarcodeAnalyzer barcodeAnalyzer = new BarcodeAnalyzer(); BarcodeAnalyzer barcodeAnalyzer = new BarcodeAnalyzer();
barcodeAnalyzer.process(path.toFile(), 0, 5); barcodeAnalyzer.process(path.toFile(), 0);
for (Result result : barcodeAnalyzer.getResultList()) { for (Map.Entry<String, String> entry : barcodeAnalyzer.getResult()) {
logger.log(Level.INFO, "barcodeFormat = " + result.getBarcodeFormat() + " value = " + result.getText()); logger.log(Level.INFO, "barcode format = " + entry.getKey() + " value = " + entry.getValue());
} }
} }
} }

View file

@ -28,6 +28,7 @@ public class DocumentAnalyzerTest {
documentAnalyzer.process(path.toFile()); documentAnalyzer.process(path.toFile());
logger.log(Level.INFO, "result = " + documentAnalyzer.getResult()); logger.log(Level.INFO, "result = " + documentAnalyzer.getResult());
logger.log(Level.INFO, "isvalid = " + documentAnalyzer.isValid()); logger.log(Level.INFO, "isvalid = " + documentAnalyzer.isValid());
logger.log(Level.INFO, "colorspaces = " + documentAnalyzer.getColorSpaces());
logger.log(Level.INFO, "iscolor = " + documentAnalyzer.isColor()); logger.log(Level.INFO, "iscolor = " + documentAnalyzer.isColor());
logger.log(Level.INFO, "isgray = " + documentAnalyzer.isGray()); logger.log(Level.INFO, "isgray = " + documentAnalyzer.isGray());
logger.log(Level.INFO, "isA4 = " + documentAnalyzer.isA4()); logger.log(Level.INFO, "isA4 = " + documentAnalyzer.isA4());