diff --git a/jhotdraw-core/src/main/java/org/jhotdraw/draw/io/ImageFormatProvider.java b/jhotdraw-core/src/main/java/org/jhotdraw/draw/io/ImageFormatProvider.java new file mode 100644 index 000000000..e159358a7 --- /dev/null +++ b/jhotdraw-core/src/main/java/org/jhotdraw/draw/io/ImageFormatProvider.java @@ -0,0 +1,72 @@ +/* + * @(#)ImageFormatProvider.java + * + * Copyright (c) 2025 The authors and contributors of JHotDraw. + * You may not use, copy or modify this file, except in compliance with the + * accompanying license terms. + */ +package org.jhotdraw.draw.io; + +import org.jhotdraw.draw.figure.ImageHolderFigure; + +/** + * A provider interface for image format support. + * + *

Implementations of this interface define how to create input and output + * formats for specific image types (PNG, JPEG, GIF, etc.).

+ * + *

To add support for a new image format:

+ *
    + *
  1. Implement this interface
  2. + *
  3. Register with {@link ImageFormatRegistry#registerProvider(ImageFormatProvider)}
  4. + *
  5. Or add to META-INF/services/org.jhotdraw.draw.io.ImageFormatProvider for SPI
  6. + *
+ * + * @author JHotDraw + * @version $Id$ + */ +public interface ImageFormatProvider { + + /** + * Returns the format name (e.g., "PNG", "JPEG"). + * + * @return the format name + */ + String getFormatName(); + + /** + * Returns a human-readable description of the format. + * + * @return the format description + */ + String getDescription(); + + /** + * Returns the file extensions supported by this format. + * + * @return array of file extensions (without dots) + */ + String[] getFileExtensions(); + + /** + * Returns the MIME types supported by this format. + * + * @return array of MIME types + */ + String[] getMimeTypes(); + + /** + * Creates an input format for reading images of this type. + * + * @param prototype the prototype figure for holding the image + * @return an InputFormat instance + */ + InputFormat createInputFormat(ImageHolderFigure prototype); + + /** + * Creates an output format for writing images of this type. + * + * @return an OutputFormat instance, or null if output is not supported + */ + OutputFormat createOutputFormat(); +} diff --git a/jhotdraw-core/src/main/java/org/jhotdraw/draw/io/ImageFormatRegistry.java b/jhotdraw-core/src/main/java/org/jhotdraw/draw/io/ImageFormatRegistry.java new file mode 100644 index 000000000..7a5ca9f7e --- /dev/null +++ b/jhotdraw-core/src/main/java/org/jhotdraw/draw/io/ImageFormatRegistry.java @@ -0,0 +1,129 @@ +/* + * @(#)ImageFormatRegistry.java + * + * Copyright (c) 2025 The authors and contributors of JHotDraw. + * You may not use, copy or modify this file, except in compliance with the + * accompanying license terms. + */ +package org.jhotdraw.draw.io; + +import org.jhotdraw.draw.figure.ImageHolderFigure; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ServiceLoader; + +/** + * A registry for image format handlers that provides a modular way to + * add support for various image formats (PNG, JPEG, GIF, etc.). + * + *

This class uses the Service Provider Interface (SPI) pattern to allow + * dynamic registration of image format handlers.

+ * + *

Usage:

+ *
+ * // Get all registered input formats for a prototype figure
+ * List<InputFormat> formats = ImageFormatRegistry.getInputFormats(myImageFigure);
+ * drawing.getInputFormats().addAll(formats);
+ * 
+ * + * @author JHotDraw + * @version $Id$ + */ +public final class ImageFormatRegistry { + + private static final List providers = new ArrayList<>(); + + static { + // Register default providers + registerProvider(new PngFormatProvider()); + registerProvider(new JpegFormatProvider()); + + // Load additional providers via SPI + ServiceLoader loader = ServiceLoader.load(ImageFormatProvider.class); + for (ImageFormatProvider provider : loader) { + registerProvider(provider); + } + } + + private ImageFormatRegistry() { + // Prevent instantiation + } + + /** + * Registers a new image format provider. + * + * @param provider the provider to register + */ + public static void registerProvider(ImageFormatProvider provider) { + if (provider != null && !providers.contains(provider)) { + providers.add(provider); + } + } + + /** + * Unregisters an image format provider. + * + * @param provider the provider to unregister + */ + public static void unregisterProvider(ImageFormatProvider provider) { + providers.remove(provider); + } + + /** + * Returns all registered providers. + * + * @return an unmodifiable list of providers + */ + public static List getProviders() { + return Collections.unmodifiableList(providers); + } + + /** + * Creates input formats for all registered image format providers. + * + * @param prototype the prototype figure to use for creating image holders + * @return a list of input formats + */ + public static List createInputFormats(ImageHolderFigure prototype) { + List formats = new ArrayList<>(); + for (ImageFormatProvider provider : providers) { + formats.add(provider.createInputFormat(prototype)); + } + return formats; + } + + /** + * Creates output formats for all registered image format providers. + * + * @return a list of output formats + */ + public static List createOutputFormats() { + List formats = new ArrayList<>(); + for (ImageFormatProvider provider : providers) { + OutputFormat format = provider.createOutputFormat(); + if (format != null) { + formats.add(format); + } + } + return formats; + } + + /** + * Checks if a specific format is supported. + * + * @param extension the file extension (e.g., "png", "jpg") + * @return true if the format is supported + */ + public static boolean isFormatSupported(String extension) { + String ext = extension.toLowerCase(); + for (ImageFormatProvider provider : providers) { + for (String supported : provider.getFileExtensions()) { + if (supported.equalsIgnoreCase(ext)) { + return true; + } + } + } + return false; + } +} diff --git a/jhotdraw-core/src/main/java/org/jhotdraw/draw/io/JpegFormatProvider.java b/jhotdraw-core/src/main/java/org/jhotdraw/draw/io/JpegFormatProvider.java new file mode 100644 index 000000000..894a77a4c --- /dev/null +++ b/jhotdraw-core/src/main/java/org/jhotdraw/draw/io/JpegFormatProvider.java @@ -0,0 +1,64 @@ +/* + * @(#)JpegFormatProvider.java + * + * Copyright (c) 2025 The authors and contributors of JHotDraw. + * You may not use, copy or modify this file, except in compliance with the + * accompanying license terms. + */ +package org.jhotdraw.draw.io; + +import org.jhotdraw.draw.figure.ImageHolderFigure; +import java.awt.image.BufferedImage; + +/** + * Provider for JPEG (Joint Photographic Experts Group) image format. + * + *

JPEG uses lossy compression, making it ideal for photographs + * and images with smooth color gradients. Supports both .jpg and .jpeg extensions.

+ * + * @author JHotDraw + * @version $Id$ + */ +public class JpegFormatProvider implements ImageFormatProvider { + + @Override + public String getFormatName() { + return "JPEG"; + } + + @Override + public String getDescription() { + return "Joint Photographic Experts Group (JPEG)"; + } + + @Override + public String[] getFileExtensions() { + return new String[]{"jpg", "jpeg"}; + } + + @Override + public String[] getMimeTypes() { + return new String[]{"image/jpeg", "image/jpg"}; + } + + @Override + public InputFormat createInputFormat(ImageHolderFigure prototype) { + return new ImageInputFormat( + prototype, + getFormatName(), + getDescription(), + getFileExtensions(), + getMimeTypes() + ); + } + + @Override + public OutputFormat createOutputFormat() { + return new ImageOutputFormat( + "JPG", + getDescription(), + "jpg", + BufferedImage.TYPE_INT_RGB + ); + } +} diff --git a/jhotdraw-core/src/main/java/org/jhotdraw/draw/io/PngFormatProvider.java b/jhotdraw-core/src/main/java/org/jhotdraw/draw/io/PngFormatProvider.java new file mode 100644 index 000000000..e5d0c23b7 --- /dev/null +++ b/jhotdraw-core/src/main/java/org/jhotdraw/draw/io/PngFormatProvider.java @@ -0,0 +1,64 @@ +/* + * @(#)PngFormatProvider.java + * + * Copyright (c) 2025 The authors and contributors of JHotDraw. + * You may not use, copy or modify this file, except in compliance with the + * accompanying license terms. + */ +package org.jhotdraw.draw.io; + +import org.jhotdraw.draw.figure.ImageHolderFigure; +import java.awt.image.BufferedImage; + +/** + * Provider for PNG (Portable Network Graphics) image format. + * + *

PNG supports lossless compression and transparency (alpha channel), + * making it ideal for graphics with sharp edges or text.

+ * + * @author JHotDraw + * @version $Id$ + */ +public class PngFormatProvider implements ImageFormatProvider { + + @Override + public String getFormatName() { + return "PNG"; + } + + @Override + public String getDescription() { + return "Portable Network Graphics (PNG)"; + } + + @Override + public String[] getFileExtensions() { + return new String[]{"png"}; + } + + @Override + public String[] getMimeTypes() { + return new String[]{"image/png"}; + } + + @Override + public InputFormat createInputFormat(ImageHolderFigure prototype) { + return new ImageInputFormat( + prototype, + getFormatName(), + getDescription(), + getFileExtensions(), + getMimeTypes() + ); + } + + @Override + public OutputFormat createOutputFormat() { + return new ImageOutputFormat( + getFormatName(), + getDescription(), + "png", + BufferedImage.TYPE_INT_ARGB + ); + } +} diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/io/ImageFormatRegistryTest.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/io/ImageFormatRegistryTest.java new file mode 100644 index 000000000..bf0cfa2aa --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/io/ImageFormatRegistryTest.java @@ -0,0 +1,98 @@ +/* + * @(#)ImageFormatRegistryTest.java + * + * Copyright (c) 2025 The authors and contributors of JHotDraw. + * You may not use, copy or modify this file, except in compliance with the + * accompanying license terms. + */ +package org.jhotdraw.draw.io; + +import org.jhotdraw.draw.figure.ImageFigure; +import org.testng.annotations.Test; +import java.util.List; + +import static org.testng.Assert.*; + +/** + * Tests for the modular ImageFormatRegistry. + */ +public class ImageFormatRegistryTest { + + @Test + public void testPngFormatSupported() { + assertTrue(ImageFormatRegistry.isFormatSupported("png"), + "PNG format should be supported"); + assertTrue(ImageFormatRegistry.isFormatSupported("PNG"), + "PNG format should be supported (uppercase)"); + } + + @Test + public void testJpegFormatSupported() { + assertTrue(ImageFormatRegistry.isFormatSupported("jpg"), + "JPG format should be supported"); + assertTrue(ImageFormatRegistry.isFormatSupported("jpeg"), + "JPEG format should be supported"); + } + + @Test + public void testGifFormatSupported() { + assertTrue(ImageFormatRegistry.isFormatSupported("gif"), + "GIF format should be supported"); + } + + @Test + public void testBmpFormatSupported() { + assertTrue(ImageFormatRegistry.isFormatSupported("bmp"), + "BMP format should be supported"); + } + + @Test + public void testUnsupportedFormat() { + assertFalse(ImageFormatRegistry.isFormatSupported("xyz"), + "XYZ format should not be supported"); + } + + @Test + public void testCreateInputFormats() { + ImageFigure prototype = new ImageFigure(); + List formats = ImageFormatRegistry.createInputFormats(prototype); + + assertNotNull(formats, "Input formats should not be null"); + assertTrue(formats.size() >= 4, + "Should have at least 4 input formats (PNG, JPEG, GIF, BMP)"); + } + + @Test + public void testCreateOutputFormats() { + List formats = ImageFormatRegistry.createOutputFormats(); + + assertNotNull(formats, "Output formats should not be null"); + assertTrue(formats.size() >= 4, + "Should have at least 4 output formats (PNG, JPEG, GIF, BMP)"); + } + + @Test + public void testProviderCount() { + List providers = ImageFormatRegistry.getProviders(); + + assertNotNull(providers, "Providers should not be null"); + assertTrue(providers.size() >= 4, "Should have at least 4 providers"); + } + + @Test + public void testPngProvider() { + PngFormatProvider provider = new PngFormatProvider(); + + assertEquals(provider.getFormatName(), "PNG"); + assertEquals(provider.getFileExtensions(), new String[]{"png"}); + assertEquals(provider.getMimeTypes(), new String[]{"image/png"}); + } + + @Test + public void testJpegProvider() { + JpegFormatProvider provider = new JpegFormatProvider(); + + assertEquals(provider.getFormatName(), "JPEG"); + assertEquals(provider.getFileExtensions(), new String[]{"jpg", "jpeg"}); + } +}