Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ private ImageProcessor resolve() {

private ImageProcessor create(String name) {
return switch (name) {
case "libvips" -> new LibVipsProcessor(binPath != null ? binPath : "vips");
case "libvips" -> new LibVipsProcessor(binPath);
case "imagemagick" -> new ImageMagickProcessor(binPath);
case "imageio" -> new ImageIOProcessor();
default -> throw new IllegalArgumentException("Unknown image processor: '" + name + "'");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,38 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/

import com.condation.cms.api.media.MediaFormat;
import com.condation.cms.media.CropCalculator.CropArea;
import com.google.common.base.Strings;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;

/**
* Image processor using libvips CLI ({@code vips} command).
* Crop is applied via {@code vips extract_area}, scaling via {@code vips thumbnail}.
* Image processor using libvips CLI ({@code vips} command). Crop is applied via
* {@code vips extract_area}, scaling via {@code vips thumbnail}.
*/
@Slf4j
public class LibVipsProcessor implements ImageProcessor {

private Boolean available = null;

private final String binPath;

public LibVipsProcessor () {
this("vips");
}
public LibVipsProcessor (String binPath) {
this.binPath = binPath;
}

private final String binPath;

public LibVipsProcessor() {
this("vips");
}

public LibVipsProcessor(String binPath) {
if (Strings.isNullOrEmpty(binPath)) {
binPath = "vips";
}
this.binPath = binPath;
}

@Override
public String name() {
return "libvips";
Expand Down Expand Up @@ -78,15 +83,22 @@ public void process(Path source, Path target, MediaFormat format, CropArea crop)
Path inputForScale = source;

if (crop != null) {
inputForScale = target.resolveSibling(target.getFileName() + ".crop.tmp");
String fileName = target.getFileName().toString();
String ext = fileName.substring(fileName.lastIndexOf('.'));

inputForScale = Files.createTempFile(
target.getParent(),
"crop-",
ext
);
runExtractArea(source, inputForScale, crop);
}

try {
runThumbnail(inputForScale, target, format);
} finally {
if (crop != null) {
java.nio.file.Files.deleteIfExists(inputForScale);
Files.deleteIfExists(inputForScale);
}
}
}
Expand Down Expand Up @@ -119,23 +131,47 @@ private void runThumbnail(Path source, Path target, MediaFormat format) throws I
}

/**
* libvips determines output format from the file extension.
* The target Path already has the correct extension from MediaUtils.
* libvips determines output format from the file extension. The target Path
* already has the correct extension from MediaUtils.
*/
private String formatTarget(Path target, MediaFormat format) {
return target.toString();
}

private void runCommand(List<String> cmd) throws IOException {
log.debug("libvips: {}", String.join(" ", cmd));

Process process = null;

try {
int exit = new ProcessBuilder(cmd)
process = new ProcessBuilder(cmd)
.redirectErrorStream(true)
.start()
.waitFor();
.start();

String output;
try (var reader = process.inputReader()) {
output = reader.lines()
.reduce("", (a, b) -> a + System.lineSeparator() + b);
}

int exit = process.waitFor();

if (exit != 0) {
throw new IOException("vips exited with code " + exit + " for command: " + String.join(" ", cmd));
throw new IOException("""
vips exited with code %d

Command:
%s

Output:
%s
""".formatted(exit, String.join(" ", cmd), output));
}

if (!output.isBlank()) {
log.debug("vips output:\n{}", output);
}

} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("vips interrupted", e);
Expand Down