mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-12 23:07:15 -06:00
Extras: FlatSVGIcon: support loading SVG from URL (for JPMS), URI, File or InputStream (issues #419 and #325)
also improved error handling and javadoc
This commit is contained in:
@@ -50,8 +50,11 @@ FlatLaf Change Log
|
|||||||
applications: `lighten()`, `darken()`, `saturate()`, `desaturate()`, `spin()`,
|
applications: `lighten()`, `darken()`, `saturate()`, `desaturate()`, `spin()`,
|
||||||
`tint()`, `shade()` and `luma()`.
|
`tint()`, `shade()` and `luma()`.
|
||||||
- Support defining fonts in FlatLaf properties files. (issue #384)
|
- Support defining fonts in FlatLaf properties files. (issue #384)
|
||||||
- Extras: Added class `FlatDesktop` for easy integration into macOS screen menu
|
- Extras:
|
||||||
(About, Preferences and Quit) when using Java 8.
|
- Added class `FlatDesktop` for easy integration into macOS screen menu
|
||||||
|
(About, Preferences and Quit) when using Java 8.
|
||||||
|
- `FlatSVGIcon`: Support loading SVG from `URL` (for JPMS), `URI`, `File` or
|
||||||
|
`InputStream`. (issues #419 and #325)
|
||||||
|
|
||||||
#### Fixed bugs
|
#### Fixed bugs
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ import java.awt.Rectangle;
|
|||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.RGBImageFilter;
|
import java.awt.image.RGBImageFilter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -60,6 +64,7 @@ public class FlatSVGIcon
|
|||||||
{
|
{
|
||||||
// use own SVG universe so that it can not be cleared from anywhere
|
// use own SVG universe so that it can not be cleared from anywhere
|
||||||
private static final SVGUniverse svgUniverse = new SVGUniverse();
|
private static final SVGUniverse svgUniverse = new SVGUniverse();
|
||||||
|
private static int streamNumber;
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final int width;
|
private final int width;
|
||||||
@@ -67,23 +72,30 @@ public class FlatSVGIcon
|
|||||||
private final float scale;
|
private final float scale;
|
||||||
private final boolean disabled;
|
private final boolean disabled;
|
||||||
private final ClassLoader classLoader;
|
private final ClassLoader classLoader;
|
||||||
|
private final URI uri;
|
||||||
|
|
||||||
private ColorFilter colorFilter;
|
private ColorFilter colorFilter;
|
||||||
|
|
||||||
private SVGDiagram diagram;
|
private SVGDiagram diagram;
|
||||||
private boolean dark;
|
private boolean dark;
|
||||||
|
private boolean loadFailed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an SVG icon from the given resource name.
|
* Creates an SVG icon from the given resource name.
|
||||||
* <p>
|
* <p>
|
||||||
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
|
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
|
||||||
* in the tag {@code <svg>} are used as icon size.
|
* in the tag {@code <svg>} are used as icon size.
|
||||||
|
* <p>
|
||||||
|
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
|
||||||
|
* Otherwise use {@link #FlatSVGIcon(URL)}.
|
||||||
|
* <p>
|
||||||
|
* This is cheap operation because the icon is only loaded when used.
|
||||||
*
|
*
|
||||||
* @param name the name of the SVG resource (a '/'-separated path)
|
* @param name the name of the SVG resource (a '/'-separated path; e.g. {@code "com/myapp/myicon.svg"})
|
||||||
* @see ClassLoader#getResource(String)
|
* @see ClassLoader#getResource(String)
|
||||||
*/
|
*/
|
||||||
public FlatSVGIcon( String name ) {
|
public FlatSVGIcon( String name ) {
|
||||||
this( name, -1, -1, 1, false, null );
|
this( name, -1, -1, 1, false, null, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,27 +104,37 @@ public class FlatSVGIcon
|
|||||||
* <p>
|
* <p>
|
||||||
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
|
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
|
||||||
* in the tag {@code <svg>} are used as icon size.
|
* in the tag {@code <svg>} are used as icon size.
|
||||||
|
* <p>
|
||||||
|
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
|
||||||
|
* Otherwise use {@link #FlatSVGIcon(URL)}.
|
||||||
|
* <p>
|
||||||
|
* This is cheap operation because the icon is only loaded when used.
|
||||||
*
|
*
|
||||||
* @param name the name of the SVG resource (a '/'-separated path)
|
* @param name the name of the SVG resource (a '/'-separated path; e.g. {@code "com/myapp/myicon.svg"})
|
||||||
* @param classLoader the class loader used to load the SVG resource
|
* @param classLoader the class loader used to load the SVG resource
|
||||||
* @see ClassLoader#getResource(String)
|
* @see ClassLoader#getResource(String)
|
||||||
*/
|
*/
|
||||||
public FlatSVGIcon( String name, ClassLoader classLoader ) {
|
public FlatSVGIcon( String name, ClassLoader classLoader ) {
|
||||||
this( name, -1, -1, 1, false, classLoader );
|
this( name, -1, -1, 1, false, classLoader, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an SVG icon from the given resource name with the given width and height.
|
* Creates an SVG icon from the given resource name with the given width and height.
|
||||||
* <p>
|
* <p>
|
||||||
* The icon is scaled if the given size is different to the size specified in the SVG file.
|
* The icon is scaled if the given size is different to the size specified in the SVG file.
|
||||||
|
* <p>
|
||||||
|
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
|
||||||
|
* Otherwise use {@link #FlatSVGIcon(URL)}.
|
||||||
|
* <p>
|
||||||
|
* This is cheap operation because the icon is only loaded when used.
|
||||||
*
|
*
|
||||||
* @param name the name of the SVG resource (a '/'-separated path)
|
* @param name the name of the SVG resource (a '/'-separated path; e.g. {@code "com/myapp/myicon.svg"})
|
||||||
* @param width the width of the icon
|
* @param width the width of the icon
|
||||||
* @param height the height of the icon
|
* @param height the height of the icon
|
||||||
* @see ClassLoader#getResource(String)
|
* @see ClassLoader#getResource(String)
|
||||||
*/
|
*/
|
||||||
public FlatSVGIcon( String name, int width, int height ) {
|
public FlatSVGIcon( String name, int width, int height ) {
|
||||||
this( name, width, height, 1, false, null );
|
this( name, width, height, 1, false, null, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,15 +142,20 @@ public class FlatSVGIcon
|
|||||||
* The SVG file is loaded from the given class loader.
|
* The SVG file is loaded from the given class loader.
|
||||||
* <p>
|
* <p>
|
||||||
* The icon is scaled if the given size is different to the size specified in the SVG file.
|
* The icon is scaled if the given size is different to the size specified in the SVG file.
|
||||||
|
* <p>
|
||||||
|
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
|
||||||
|
* Otherwise use {@link #FlatSVGIcon(URL)}.
|
||||||
|
* <p>
|
||||||
|
* This is cheap operation because the icon is only loaded when used.
|
||||||
*
|
*
|
||||||
* @param name the name of the SVG resource (a '/'-separated path)
|
* @param name the name of the SVG resource (a '/'-separated path; e.g. {@code "com/myapp/myicon.svg"})
|
||||||
* @param width the width of the icon
|
* @param width the width of the icon
|
||||||
* @param height the height of the icon
|
* @param height the height of the icon
|
||||||
* @param classLoader the class loader used to load the SVG resource
|
* @param classLoader the class loader used to load the SVG resource
|
||||||
* @see ClassLoader#getResource(String)
|
* @see ClassLoader#getResource(String)
|
||||||
*/
|
*/
|
||||||
public FlatSVGIcon( String name, int width, int height, ClassLoader classLoader ) {
|
public FlatSVGIcon( String name, int width, int height, ClassLoader classLoader ) {
|
||||||
this( name, width, height, 1, false, classLoader );
|
this( name, width, height, 1, false, classLoader, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -137,13 +164,18 @@ public class FlatSVGIcon
|
|||||||
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
|
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
|
||||||
* in the tag {@code <svg>} are used as base icon size, which is multiplied
|
* in the tag {@code <svg>} are used as base icon size, which is multiplied
|
||||||
* by the given scale factor.
|
* by the given scale factor.
|
||||||
|
* <p>
|
||||||
|
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
|
||||||
|
* Otherwise use {@link #FlatSVGIcon(URL)}.
|
||||||
|
* <p>
|
||||||
|
* This is cheap operation because the icon is only loaded when used.
|
||||||
*
|
*
|
||||||
* @param name the name of the SVG resource (a '/'-separated path)
|
* @param name the name of the SVG resource (a '/'-separated path; e.g. {@code "com/myapp/myicon.svg"})
|
||||||
* @param scale the amount by which the icon size is scaled
|
* @param scale the amount by which the icon size is scaled
|
||||||
* @see ClassLoader#getResource(String)
|
* @see ClassLoader#getResource(String)
|
||||||
*/
|
*/
|
||||||
public FlatSVGIcon( String name, float scale ) {
|
public FlatSVGIcon( String name, float scale ) {
|
||||||
this( name, -1, -1, scale, false, null );
|
this( name, -1, -1, scale, false, null, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,23 +185,107 @@ public class FlatSVGIcon
|
|||||||
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
|
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
|
||||||
* in the tag {@code <svg>} are used as base icon size, which is multiplied
|
* in the tag {@code <svg>} are used as base icon size, which is multiplied
|
||||||
* by the given scale factor.
|
* by the given scale factor.
|
||||||
|
* <p>
|
||||||
|
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
|
||||||
|
* Otherwise use {@link #FlatSVGIcon(URL)}.
|
||||||
|
* <p>
|
||||||
|
* This is cheap operation because the icon is only loaded when used.
|
||||||
*
|
*
|
||||||
* @param name the name of the SVG resource (a '/'-separated path)
|
* @param name the name of the SVG resource (a '/'-separated path; e.g. {@code "com/myapp/myicon.svg"})
|
||||||
* @param scale the amount by which the icon size is scaled
|
* @param scale the amount by which the icon size is scaled
|
||||||
* @param classLoader the class loader used to load the SVG resource
|
* @param classLoader the class loader used to load the SVG resource
|
||||||
* @see ClassLoader#getResource(String)
|
* @see ClassLoader#getResource(String)
|
||||||
*/
|
*/
|
||||||
public FlatSVGIcon( String name, float scale, ClassLoader classLoader ) {
|
public FlatSVGIcon( String name, float scale, ClassLoader classLoader ) {
|
||||||
this( name, -1, -1, scale, false, classLoader );
|
this( name, -1, -1, scale, false, classLoader, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatSVGIcon( String name, int width, int height, float scale, boolean disabled, ClassLoader classLoader ) {
|
/**
|
||||||
|
* Creates an SVG icon from the given URL.
|
||||||
|
* <p>
|
||||||
|
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
|
||||||
|
* in the tag {@code <svg>} are used as icon size.
|
||||||
|
* <p>
|
||||||
|
* This method is useful if using Java modules and the package containing the icon
|
||||||
|
* is not opened in {@code module-info.java}.
|
||||||
|
* E.g. {@code new FlatSVGIcon( getClass().getResource( "/com/myapp/myicon.svg" ) )}.
|
||||||
|
* <p>
|
||||||
|
* This is cheap operation because the icon is only loaded when used.
|
||||||
|
*
|
||||||
|
* @param url the URL of the SVG resource
|
||||||
|
* @see ClassLoader#getResource(String)
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public FlatSVGIcon( URL url ) {
|
||||||
|
this( null, -1, -1, 1, false, null, url2uri( url ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an SVG icon from the given URI.
|
||||||
|
* <p>
|
||||||
|
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
|
||||||
|
* in the tag {@code <svg>} are used as icon size.
|
||||||
|
* <p>
|
||||||
|
* This is cheap operation because the icon is only loaded when used.
|
||||||
|
*
|
||||||
|
* @param uri the URI of the SVG resource
|
||||||
|
* @see ClassLoader#getResource(String)
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public FlatSVGIcon( URI uri ) {
|
||||||
|
this( null, -1, -1, 1, false, null, uri );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an SVG icon from the given file.
|
||||||
|
* <p>
|
||||||
|
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
|
||||||
|
* in the tag {@code <svg>} are used as icon size.
|
||||||
|
* <p>
|
||||||
|
* This is cheap operation because the icon is only loaded when used.
|
||||||
|
*
|
||||||
|
* @param file the SVG file
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public FlatSVGIcon( File file ) {
|
||||||
|
this( null, -1, -1, 1, false, null, file.toURI() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an SVG icon from the given input stream.
|
||||||
|
* <p>
|
||||||
|
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
|
||||||
|
* in the tag {@code <svg>} are used as icon size.
|
||||||
|
* <p>
|
||||||
|
* The input stream is loaded, parsed and closed immediately.
|
||||||
|
*
|
||||||
|
* @param in the input stream for reading a SVG resource
|
||||||
|
* @throws IOException if an I/O exception occurs
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public FlatSVGIcon( InputStream in ) throws IOException {
|
||||||
|
this( null, -1, -1, 1, false, null, loadFromStream( in ) );
|
||||||
|
|
||||||
|
// since the input stream is already loaded and parsed,
|
||||||
|
// get diagram here and remove it from SVGUniverse cache
|
||||||
|
update();
|
||||||
|
svgUniverse.removeDocument( uri );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static URI loadFromStream( InputStream in ) throws IOException {
|
||||||
|
try( InputStream in2 = in ) {
|
||||||
|
return svgUniverse.loadSVG( in2, "/flatlaf-stream-" + (streamNumber++) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FlatSVGIcon( String name, int width, int height, float scale, boolean disabled, ClassLoader classLoader, URI uri ) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.scale = scale;
|
this.scale = scale;
|
||||||
this.disabled = disabled;
|
this.disabled = disabled;
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
|
this.uri = uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -248,7 +364,7 @@ public class FlatSVGIcon
|
|||||||
if( width == this.width && height == this.height )
|
if( width == this.width && height == this.height )
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader );
|
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader, uri );
|
||||||
icon.colorFilter = colorFilter;
|
icon.colorFilter = colorFilter;
|
||||||
icon.diagram = diagram;
|
icon.diagram = diagram;
|
||||||
icon.dark = dark;
|
icon.dark = dark;
|
||||||
@@ -267,7 +383,7 @@ public class FlatSVGIcon
|
|||||||
if( scale == this.scale )
|
if( scale == this.scale )
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader );
|
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader, uri );
|
||||||
icon.colorFilter = colorFilter;
|
icon.colorFilter = colorFilter;
|
||||||
icon.diagram = diagram;
|
icon.diagram = diagram;
|
||||||
icon.dark = dark;
|
icon.dark = dark;
|
||||||
@@ -286,7 +402,7 @@ public class FlatSVGIcon
|
|||||||
if( disabled )
|
if( disabled )
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, true, classLoader );
|
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, true, classLoader, uri );
|
||||||
icon.colorFilter = colorFilter;
|
icon.colorFilter = colorFilter;
|
||||||
icon.diagram = diagram;
|
icon.diagram = diagram;
|
||||||
icon.dark = dark;
|
icon.dark = dark;
|
||||||
@@ -324,19 +440,39 @@ public class FlatSVGIcon
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void update() {
|
private void update() {
|
||||||
|
if( loadFailed )
|
||||||
|
return;
|
||||||
|
|
||||||
if( dark == isDarkLaf() && diagram != null )
|
if( dark == isDarkLaf() && diagram != null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dark = isDarkLaf();
|
dark = isDarkLaf();
|
||||||
URL url = getIconURL( name, dark );
|
|
||||||
if( url == null & dark )
|
// SVGs already loaded via url or input stream can not have light/dark variants
|
||||||
url = getIconURL( name, false );
|
if( uri != null && diagram != null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
URI uri = this.uri;
|
||||||
|
if( uri == null ) {
|
||||||
|
URL url = getIconURL( name, dark );
|
||||||
|
if( url == null & dark )
|
||||||
|
url = getIconURL( name, false );
|
||||||
|
|
||||||
|
if( url == null ) {
|
||||||
|
loadFailed = true;
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: resource '" + name + "' not found (if using Java modules, check whether icon package is opened in module-info.java)", null );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uri = url2uri( url );
|
||||||
|
}
|
||||||
|
|
||||||
// load/get image
|
// load/get image
|
||||||
try {
|
diagram = svgUniverse.getDiagram( uri );
|
||||||
diagram = svgUniverse.getDiagram( url.toURI() );
|
|
||||||
} catch( URISyntaxException ex ) {
|
if( diagram == null ) {
|
||||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load SVG icon '" + url + "'.", ex );
|
loadFailed = true;
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: failed to load '" + uri + "'", null );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,6 +623,14 @@ public class FlatSVGIcon
|
|||||||
return MultiResolutionImageSupport.create( 0, dimensions, producer );
|
return MultiResolutionImageSupport.create( 0, dimensions, producer );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static URI url2uri( URL url ) {
|
||||||
|
try {
|
||||||
|
return url.toURI();
|
||||||
|
} catch( URISyntaxException ex ) {
|
||||||
|
throw new IllegalArgumentException( ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static Boolean darkLaf;
|
private static Boolean darkLaf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user