## Summary
- introduce a shared line art conversion interface and proprietary
ImageMagick-backed implementation
- have the compress controller optionally autowire the enterprise
service before running per-image line art processing
- remove ImageMagick command details from core by delegating conversions
through the proprietary service

## Testing
- not run (not requested)


------
[Codex
Task](https://chatgpt.com/codex/tasks/task_b_6928aecceaf083289a9269b1ca99307e)

---------

Co-authored-by: James Brunton <[email protected]>
This commit is contained in:
Anthony Stirling
2025-12-15 11:14:10 +00:00
committed by GitHub
co-authored by James Brunton
parent 33188815da
commit 5f72c05623
24 changed files with 445 additions and 75 deletions
@@ -0,0 +1,81 @@
package stirling.software.proprietary.service;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.imageio.ImageIO;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.service.LineArtConversionService;
import stirling.software.common.util.ProcessExecutor;
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
@Slf4j
@Service
public class ImageMagickLineArtConversionService implements LineArtConversionService {
@Override
public PDImageXObject convertImageToLineArt(
PDDocument doc, PDImageXObject originalImage, double threshold, int edgeLevel)
throws IOException {
Path inputImage = Files.createTempFile("lineart_image_input_", ".png");
Path outputImage = Files.createTempFile("lineart_image_output_", ".tiff");
try {
ImageIO.write(originalImage.getImage(), "png", inputImage.toFile());
List<String> command = new ArrayList<>();
command.add("magick");
command.add(inputImage.toString());
command.add("-colorspace");
command.add("Gray");
// Edge-aware line art conversion using ImageMagick's built-in operators.
// -edge/-negate/-normalize are standard convert options (IM v6+/v7) that
// accentuate outlines before thresholding to a bilevel image.
command.add("-edge");
command.add(String.valueOf(edgeLevel));
command.add("-negate");
command.add("-normalize");
command.add("-type");
command.add("Bilevel");
command.add("-threshold");
command.add(String.format(Locale.ROOT, "%.1f%%", threshold));
command.add("-compress");
command.add("Group4");
command.add(outputImage.toString());
ProcessExecutorResult result =
ProcessExecutor.getInstance(ProcessExecutor.Processes.IMAGEMAGICK)
.runCommandWithOutputHandling(command);
if (result.getRc() != 0) {
log.warn(
"ImageMagick line art conversion failed with return code: {}",
result.getRc());
throw new IOException("ImageMagick line art conversion failed");
}
byte[] convertedBytes = Files.readAllBytes(outputImage);
return PDImageXObject.createFromByteArray(
doc, convertedBytes, originalImage.getCOSObject().toString());
} catch (Exception e) {
log.warn("ImageMagick line art conversion failed", e);
throw new IOException("ImageMagick line art conversion failed", e);
} finally {
Files.deleteIfExists(inputImage);
Files.deleteIfExists(outputImage);
}
}
}