← All tasks
javaclaude-code/java-t1 #18Not a task: repair changed code

Image Format Converter (java, written by Claude Code)

envgap__claude-code__java-t1-18

Written by a coding agent; not on GitHubWritten 2026-02-27

01 / FAILURE SIGNATURE

As the study recorded it

cannot find symbol: class WebPImageWriterSpi + source in project root
Not a benchmark task.
  • Its repair changed source code, so it is not an environment task.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
pom.xml
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

claude-code/java-t1 #18 · read the task the agent was given
Claude Code wrote this java project from the task below. It does not run on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: Image Format Converter

Write a program that converts images between common formats (PNG, JPEG, BMP, TIFF, WebP) with configurable quality settings, preserving metadata where possible.

FUNCTIONAL REQUIREMENTS:
- Accept an image file path and target format as command-line arguments
- Support conversions between: PNG, JPEG, BMP, TIFF, and WebP formats
- Support a --quality flag for lossy formats (JPEG, WebP) with a value from 1-100 (default: 85)
- Support a --resize flag to resize during conversion using WxH syntax (e.g., --resize 800x600), preserving aspect ratio by default
- Support --maintain-aspect flag (default true) to control whether resizing preserves the original aspect ratio by fitting within the specified dimensions
- Preserve EXIF metadata during conversion where the target format supports it, with a --strip-metadata flag to optionally remove it
- Support alpha channel handling: when converting from formats with transparency (PNG, WebP) to formats without (JPEG, BMP), use a configurable background color via --background flag (default: white)
- Support batch conversion of all images in a directory via --batch flag, optionally filtering by input format with --filter flag
- Print conversion details to console: input format, output format, original dimensions, new dimensions, file size before and after, compression ratio
- Save the converted image to a path specified by --output flag (default: same name with new extension)
- If no input is given, generate a sample 800x600 PNG image with gradients, text, and transparency, then convert it to all other supported formats and display a comparison table
- Handle errors: unsupported formats, corrupted images, insufficient disk space, invalid resize dimensions

Create a complete Java project for a clean Ubuntu 22.04 machine with only JDK 17+ installed. Include:
- Source code
- pom.xml with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

ImageConverter.java
import com.twelvemonkeys.imageio.plugins.webp.WebPImageReaderSpi;
import com.twelvemonkeys.imageio.plugins.webp.WebPImageWriterSpi;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriterSpi;

import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Image Format Converter supporting PNG, JPEG, BMP, TIFF, and WebP.
 * Provides quality settings, resize, and metadata preservation.
 */
public class ImageConverter {

    private static final Set<String> SUPPORTED_EXTENSIONS = Set.of(
            "png", "jpg", "jpeg", "bmp", "tiff", "tif", "webp"
    );

    private static final Map<String, String> EXTENSION_TO_FORMAT = Map.of(
            "png", "PNG",
            "jpg", "JPEG",
            "jpeg", "JPEG",
            "bmp", "BMP",
            "tiff", "TIFF",
            "tif", "TIFF",
            "webp", "WebP"
    );

    /**
     * Result object returned after conversion.
     */
    public static class ConversionResult {
        private final String outputPath;
        private final long fileSizeBytes;
        private final int width;
        private final int height;
        private final String format;

        public ConversionResult(String outputPath, long fileSizeBytes, int width, int height, String format) {
            this.outputPath = outputPath;
            this.fileSizeBytes = fileSizeBytes;
            this.width = width;
            this.height = height;
            this.format = format;
        }

        public String getOutputPath() { return outputPath; }
        public long getFileSizeBytes() { return fileSizeBytes; }
        public int getWidth() { return width; }
        public int getHeight() { return height; }
        public String getFormat() { return format; }

        @Override
        public String toString() {
            return String.format("ConversionResult{path='%s', format='%s', %dx%d, %,d bytes}",
                    outputPath, format, width, height, fileSizeBytes);
        }
    }

    /**
     * Options for image conversion.
     */
    public static class ConversionOptions {
        private int quality = 85;
        private int resizeWidth = -1;
        private int resizeHeight = -1;
        private boolean preserveMetadata = true;
        private boolean verbose = false;

        public ConversionOptions() {}

        public ConversionOptions quality(int quality) {
            this.quality = Math.max(1, Math.min(100, quality));
            return this;
        }

        public ConversionOptions resize(int width, int height) {
            this.resizeWidth = width;
            this.resizeHeight = height;
            return this;
        }

        public ConversionOptions preserveMetadata(boolean preserve) {
            this.preserveMetadata = preserve;
            return this;
        }

        public ConversionOptions verbose(boolean verbose) {
            this.verbose = verbose;
            return this;
        }

        public int getQuality() { return quality; }
        public int getResizeWidth() { return resizeWidth; }
        public int getResizeHeight() { return resizeHeight; }
        public boolean isPreserveMetadata() { return preserveMetadata; }
        public boolean isVerbose() { return verbose; }
    }

    /**
     * Get the file extension in lowercase.
     */
    private static String getExtension(String filename) {
        int lastDot = filename.lastIndexOf('.');
        if (lastDot < 0) {
            return "";
        }
        return filename.substring(lastDot + 1).toLowerCase();
    }

    /**
     * Get the format name from a file extension.
     */
    private static String getFormatFromExtension(String filename) {
        String ext = getExtension(filename);
        String format = EXTENSION_TO_FORMAT.get(ext);
        if (format == null) {
            throw new IllegalArgumentException("Unsupported format: " + ext
                    + ". Supported: " + SUPPORTED_EXTENSIONS);
        }
        return format;
    }

    /**
     * Convert a single image file.
     *
     * @param inputPath  Path to the source image.
     * @param outputPath Path for the converted image.
     * @param options    Conversion options.
     * @return ConversionResult with details about the output.
     * @throws IOException If reading or writing fails.
     */
    public static ConversionResult convert(String inputPath, String outputPath,
                                           ConversionOptions options) throws IOException {
        File inputFile = new File(inputPath);
        if (!inputFile.exists()) {
            throw new IOException("Input file not found: " + inputPath);
        }

        String outputFormat = getFormatFromExtension(outputPath);

        if (options.isVerbose()) {
            System.out.println("Opening: " + inputPath);
        }

        // Read image with metadata
        ImageInputStream iis = ImageIO.createImageInputStream(inputFile);
        Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
        if (!readers.hasNext()) {
            iis.close();
            throw new IOException("No suitable image reader found for: " + inputPath);
        }

        ImageReader reader = readers.next();
        reader.setInput(iis);

        // Read metadata
        IIOMetadata metadata = null;
        if (options.isPreserveMetadata()) {
            try {
                metadata = reader.getImageMetadata(0);
            } catch (Exception e) {
                if (options.isVerbose()) {
                    System.out.println("Warning: Could not read metadata: " + e.getMessage());
                }
            }
        }

        BufferedImage image = reader.read(0);
        reader.dispose();
        iis.close();

        if (options.isVerbose()) {
            System.out.println("Original size: " + image.getWidth() + "x" + image.getHeight());
            System.out.println("Original type: " + image.getType());
        }

        // Resize if requested
        if (options.getResizeWidth() > 0 && options.getResizeHeight() > 0) {
            image = resizeImage(image, options.getResizeWidth(), options.getResizeHeight());
            if (options.isVerbose()) {
                System.out.println("Resized to: " + options.getResizeWidth() + "x" + options.getResizeHeight());
            }
        }

        // Handle JPEG compatibility (no alpha channel)
        if ("JPEG".equals(outputFormat) && image.getColorModel().hasAlpha()) {
            BufferedImage rgbImage = new BufferedImage(
                    image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = rgbImage.createGraphics();
            g2d.setColor(Color.WHITE);
            g2d.fillRect(0, 0, image.getWidth(), image.getHeight());
            g2d.drawImage(image, 0, 0, null);
            g2d.dispose();
            image = rgbImage;
            if (options.isVerbose()) {
                System.out.println("Converted to RGB for JPEG compatibility.");
            }
        }

        // Ensure output directory exists
        File outputFile = new File(outputPath);
        if (outputFile.getParentFile() != null) {
            outputFile.getParentFile().mkdirs();
        }

        // Write image
        writeImage(image, outputFile, outputFormat, options, metadata);

        long fileSize = outputFile.length();
        if (options.isVerbose()) {
            System.out.println("Saved: " + outputFile.getAbsolutePath());
            System.out.printf("Output file size: %,d bytes%n", fileSize);
        }

        return new ConversionResult(
                outputFile.getAbsolutePath(),
                fileSize,
                image.getWidth(),
                image.getHeight(),
                outputFormat
        );
    }

    /**
     * Write an image to a file with the specified format and options.
     */
    private static void writeImage(BufferedImage image, File outputFile, String format,
                                   ConversionOptions options, IIOMetadata metadata) throws IOException {
        Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
        if (!writers.hasNext()) {
            throw new IOException("No suitable image writer found for format: " + format);
        }

        ImageWriter writer = writers.next();
        ImageWriteParam writeParam = writer.getDefaultWriteParam();

        // Set quality for lossy formats
        if ("JPEG".equals(format) || "WebP".equals(format)) {
            if (writeParam.canWriteCompressed()) {
                writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                String[] compressionTypes = writeParam.getCompressionTypes();
                if (compressionTypes != null && compressionTypes.length > 0) {
                    writeParam.setCompressionType(compressionTypes[0]);
                }
                writeParam.setCompressionQuality(options.getQuality() / 100.0f);
            }
        }

        ImageOutputStream ios = ImageIO.createImageOutputStream(outputFile);
        writer.setOutput(ios);

        IIOMetadata outputMetadata = null;
        if (options.isPreserveMetadata() && metadata != null) {
            try {
                outputMetadata = writer.convertImageMetadata(metadata, ImageTypeSpecifier.createFromRenderedImage(image), writeParam);
            } catch (Exception e) {
                if (options.isVerbose()) {
                    System.out.println("Warning: Could not convert metadata: " + e.getMessage());
                }
            }
        }

        IIOImage iioImage = new IIOImage(image, null, outputMetadata);
        writer.write(null, iioImage, writeParam);
        writer.dispose();
        ios.close();
    }

    /**
     * Resize an image using high-quality bilinear interpolation.
     */
    private static BufferedImage resizeImage(BufferedImage original, int targetWidth, int targetHeight) {
        BufferedImage resized = new BufferedImage(targetWidth, targetHeight, original.getType());
        Graphics2D g2d = resized.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.drawImage(original, 0, 0, targetWidth, targetHeight, null);
        g2d.dispose();
        return resized;
    }

    /**
     * Batch convert all supported images in a directory.
     *
     * @param inputDir     Path to the source directory.
     * @param outputDir    Path to the output directory.
     * @param targetFormat Target file extension (e.g., "png", "jpg").
     * @param options      Conversion options.
     * @return List of ConversionResult objects.
     * @throws IOException If directory access fails.
     */
    public static List<ConversionResult> batchConvert(String inputDir, String outputDir,
                                                       String targetFormat, ConversionOptions options) throws IOException {
        Path inputPath = Paths.get(inputDir);
        if (!Files.isDirectory(inputPath)) {
            throw new IOException("Input directory not found: " + inputDir);
        }

        Files.createDirectories(Paths.get(outputDir));

        String targetExt = targetFormat.startsWith(".") ? targetFormat.substring(1) : targetFormat;
        if (!SUPPORTED_EXTENSIONS.contains(targetExt.toLowerCase())) {
            throw new IllegalArgumentException("Unsupported target format: " + targetExt);
        }

        List<ConversionResult> results = new ArrayList<>();

        try (Stream<Path> stream = Files.list(inputPath)) {
            List<Path> files = stream
                    .filter(Files::isRegularFile)
                    .filter(p -> SUPPORTED_EXTENSIONS.contains(getExtension(p.getFileName().toString())))
                    .sorted()
                    .collect(Collectors.toList());

            for (Path file : files) {
                String baseName = file.getFileName().toString();
                int dotIndex = baseName.lastIndexOf('.');
                if (dotIndex > 0) {
                    baseName = baseName.substring(0, dotIndex);
                }
                String outputFileName = baseName + "." + targetExt;
                String outputFilePath = Paths.get(outputDir, outputFileName).toString();

                try {
                    ConversionResult result = convert(file.toString(), outputFilePath, options);
                    results.add(result);
                } catch (Exception e) {
                    System.err.println("Error converting " + file.getFileName() + ": " + e.getMessage());
                }
            }
        }

        return results;
    }

    /**
     * Display image information.
     */
    public static void printImageInfo(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new IOException("File not found: " + filePath);
        }

        BufferedImage image = ImageIO.read(file);
        if (image == null) {
            throw new IOException("Could not read image: " + filePath);
        }

        System.out.println("File: " + file.getAbsolutePath());
        System.out.println("Format: " + getFormatFromExtension(filePath));
        System.out.println("Dimensions: " + image.getWidth() + "x" + image.getHeight());
        System.out.println("Color model: " + image.getColorModel());
        System.out.printf("File size: %,d bytes%n", file.length());

        // Read metadata
        ImageInputStream iis = ImageIO.createImageInputStream(file);
        Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
        if (readers.hasNext()) {
            ImageReader reader = readers.next();
            reader.setInput(iis);
            try {
                IIOMetadata meta = reader.getImageMetadata(0);
                if (meta != null) {
                    String[] formatNames = meta.getMetadataFormatNames();
                    System.out.println("Metadata formats: " + String.join(", ", formatNames));
                }
            } catch (Exception e) {
                System.out.println("Metadata: not available");
            }
            reader.dispose();
        }
        iis.close();
    }

    /**
     * CLI entry point.
     */
    public static void main(String[] args) {
        if (args.length < 1) {
            printUsage();
            System.exit(1);
        }

        String command = args[0];

        try {
            switch (command) {
                case "convert":
                    handleConvert(args);
                    break;
                case "batch":
                    handleBatch(args);
                    break;
                case "info":
                    handleInfo(args);
                    break;
                default:
                    System.err.println("Unknown command: " + command);
                    printUsage();
                    System.exit(1);
            }
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
            System.exit(1);
        }
    }

    private static void handleConvert(String[] args) throws IOException {
        if (args.length < 3) {
            System.err.println("Usage: convert <input> <output> [options]");
            System.exit(1);
        }

        String input = args[1];
        String output = args[2];
        ConversionOptions options = parseOptions(args, 3);

        ConversionResult result = convert(input, output, options);
        System.out.println("Converted successfully: " + result);
    }

    private static void handleBatch(String[] args) throws IOException {
        if (args.length < 4) {
            System.err.println("Usage: batch <input_dir> <output_dir> <format> [options]");
            System.exit(1);
        }

        String inputDir = args[1];
        String outputDir = args[2];
        String format = args[3];
        ConversionOptions options = parseOptions(args, 4);

        List<ConversionResult> results = batchConvert(inputDir, outputDir, format, options);
        System.out.println("Batch conversion complete. " + results.size() + " file(s) converted.");
    }

    private static void handleInfo(String[] args) throws IOException {
        if (args.length < 2) {
            System.err.println("Usage: info <file>");
            System.exit(1);
        }
        printImageInfo(args[1]);
    }

    private static ConversionOptions parseOptions(String[] args, int startIndex) {
        ConversionOptions options = new ConversionOptions();
        for (int i = startIndex; i < args.length; i++) {
            switch (args[i]) {
                case "-q":
                case "--quality":
                    if (i + 1 < args.length) {
                        options.quality(Integer.parseInt(args[++i]));
                    }
                    break;
                case "-r":
                case "--resize":
                    if (i + 1 < args.length) {
                        String[] parts = args[++i].toLowerCase().split("x");
                        options.resize(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
                    }
                    break;
                case "--no-metadata":
                    options.preserveMetadata(false);
                    break;
                case "-v":
                case "--verbose":
                    options.verbose(true);
                    break;
            }
        }
        return options;
    }

    private static void printUsage() {
        System.out.println("Image Format Converter");
        System.out.println();
        System.out.println("Commands:");
        System.out.println("  convert <input> <output> [options]    Convert a single image");
        System.out.println("  batch <indir> <outdir> <fmt> [options] Batch convert directory");
        System.out.println("  info <file>                           Display image info");
        System.out.println();
        System.out.println("Options:");
        System.out.println("  -q, --quality <1-100>   Quality setting (default: 85)");
        System.out.println("  -r, --resize <WxH>      Resize (e.g., 800x600)");
        System.out.println("  --no-metadata            Strip metadata");
        System.out.println("  -v, --verbose            Verbose output");
    }
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.imageconverter</groupId>
    <artifactId>image-format-converter</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <name>Image Format Converter</name>
    <description>Converts between PNG, JPEG, BMP, TIFF, and WebP with quality settings, resize, and metadata preservation.</description>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <twelvemonkeys.version>3.10.1</twelvemonkeys.version>
    </properties>

    <dependencies>
        <!-- TwelveMonkeys ImageIO - TIFF support -->
        <dependency>
            <groupId>com.twelvemonkeys.imageio</groupId>
            <artifactId>imageio-tiff</artifactId>
            <version>${twelvemonkeys.version}</version>
        </dependency>

        <!-- TwelveMonkeys ImageIO - WebP support -->
        <dependency>
            <groupId>com.twelvemonkeys.imageio</groupId>
            <artifactId>imageio-webp</artifactId>
            <version>${twelvemonkeys.version}</version>
        </dependency>

        <!-- TwelveMonkeys ImageIO - BMP enhancements -->
        <dependency>
            <groupId>com.twelvemonkeys.imageio</groupId>
            <artifactId>imageio-bmp</artifactId>
            <version>${twelvemonkeys.version}</version>
        </dependency>

        <!-- TwelveMonkeys ImageIO - JPEG enhancements -->
        <dependency>
            <groupId>com.twelvemonkeys.imageio</groupId>
            <artifactId>imageio-jpeg</artifactId>
            <version>${twelvemonkeys.version}</version>
        </dependency>

        <!-- TwelveMonkeys ImageIO - Core -->
        <dependency>
            <groupId>com.twelvemonkeys.imageio</groupId>
            <artifactId>imageio-core</artifactId>
            <version>${twelvemonkeys.version}</version>
        </dependency>

        <!-- TwelveMonkeys ImageIO - Metadata -->
        <dependency>
            <groupId>com.twelvemonkeys.imageio</groupId>
            <artifactId>imageio-metadata</artifactId>
            <version>${twelvemonkeys.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.3.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>ImageConverter</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.5.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>ImageConverter</mainClass>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
README.md
# Image Format Converter (Java - javax.imageio + TwelveMonkeys)

An image format converter that supports PNG, JPEG, BMP, TIFF, and WebP formats with quality settings, resize capabilities, and metadata preservation.

## Dependencies

- **javax.imageio** (JDK built-in) - Core Java Image I/O framework
- **TwelveMonkeys ImageIO 3.10.1** - Extended format support for TIFF, WebP, and enhanced JPEG/BMP handling

## Build

```bash
mvn clean package
```

## Usage

### Convert a single image

```bash
java -jar target/image-format-converter-1.0.0.jar convert input.png output.jpg
java -jar target/image-format-converter-1.0.0.jar convert input.bmp output.webp -q 90
java -jar target/image-format-converter-1.0.0.jar convert input.tiff output.png -r 800x600
java -jar target/image-format-converter-1.0.0.jar convert input.jpg output.tiff --no-metadata -v
```

### Batch convert images

```bash
java -jar target/image-format-converter-1.0.0.jar batch ./input_dir ./output_dir png
java -jar target/image-format-converter-1.0.0.jar batch ./photos ./thumbnails jpg -q 75 -r 200x200
```

### Display image information

```bash
java -jar target/image-format-converter-1.0.0.jar info photo.jpg
```

## Options

| Option | Description |
|--------|-------------|
| `-q, --quality` | Quality setting (1-100, default: 85) |
| `-r, --resize` | Resize to WIDTHxHEIGHT (e.g., 800x600) |
| `--no-metadata` | Strip metadata from output |
| `-v, --verbose` | Print detailed conversion information |

## Supported Formats

| Format | Read | Write | Notes |
|--------|------|-------|-------|
| PNG | Yes | Yes | Via JDK built-in |
| JPEG | Yes | Yes | Enhanced via TwelveMonkeys |
| BMP | Yes | Yes | Enhanced via TwelveMonkeys |
| TIFF | Yes | Yes | Via TwelveMonkeys |
| WebP | Yes | Yes | Via TwelveMonkeys |