From b6baa3e71b437f7e6b320d0f4c581e2c28e5a3b1 Mon Sep 17 00:00:00 2001 From: Jmr3366 Date: Sat, 3 Jan 2026 07:45:32 -0500 Subject: [PATCH 1/4] createAsset improvements in error checking and capabilities. Resolves #5917 --- .../maptool/client/functions/TokenImage.java | 118 ++++++++++++------ 1 file changed, 78 insertions(+), 40 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/functions/TokenImage.java b/src/main/java/net/rptools/maptool/client/functions/TokenImage.java index 666b586847..5e6058bfdf 100644 --- a/src/main/java/net/rptools/maptool/client/functions/TokenImage.java +++ b/src/main/java/net/rptools/maptool/client/functions/TokenImage.java @@ -65,9 +65,6 @@ final int getValue() { public static final String SET_IMAGE = "setImage"; public static final String SET_PORTRAIT = "setTokenPortrait"; public static final String SET_HANDOUT = "setTokenHandout"; - public static final String FILE_HEADER_WEBP = "RIFF"; - public static final String FILE_HEADER_JPG = "ÿØÿà"; - public static final String FILE_HEADER_PNG = "‰PNG"; private TokenImage() { super( @@ -85,7 +82,32 @@ private TokenImage() { "getTokenOpacity", "createAsset"); } + private static boolean isPNG(byte[] b) { + return b.length >= 8 + && (b[0] & 0xFF) == 0x89 + && b[1] == 0x50 + && b[2] == 0x4E + && b[3] == 0x47; + } + + private static boolean isJPG(byte[] b) { + return b.length >= 3 + && (b[0] & 0xFF) == 0xFF + && (b[1] & 0xFF) == 0xD8 + && (b[2] & 0xFF) == 0xFF; + } + private static boolean isWEBP(byte[] b) { + return b.length >= 12 + && b[0] == 'R' + && b[1] == 'I' + && b[2] == 'F' + && b[3] == 'F' + && b[8] == 'W' + && b[9] == 'E' + && b[10] == 'B' + && b[11] == 'P'; + } /** * Gets the TokenImage instance. * @@ -191,48 +213,61 @@ public Object childEvaluate( String imageString = args.get(1).toString(); if (imageName.isEmpty() || imageString.isEmpty()) { throw new ParserException( - I18N.getText("macro.function.general.paramCannotBeEmpty", functionName)); - } else if (imageString.length() > 8) { - Asset asset; - URI uri; + I18N.getText("macro.function.general.paramCannotBeEmpty", functionName)); + } + if (imageString.length() <= 8) { + throw new ParserException( + I18N.getText("macro.function.general.wrongParamType", functionName)); + } + Asset asset; + boolean isBase64Image = false; + byte[] base64Bytes = null; + try { + base64Bytes = Base64.decode(imageString); + if (isPNG(base64Bytes) || isJPG(base64Bytes) || isWEBP(base64Bytes)) { + isBase64Image = true; + } + } catch (Exception ignore) { + } + URI uri = null; + if (!isBase64Image) { try { uri = new URI(imageString); + if (uri.getScheme() == null) { + uri = null; + } } catch (URISyntaxException e) { uri = null; } - if (uri != null && isValidAssetScheme(uri) && isValidAssetExtension(uri)) { - try { - URL url = uri.toURL(); - BufferedImage imageRAW = ImageIO.read(url); - asset = Asset.createImageAsset(imageName, imageRAW); - } catch (MalformedURLException | IllegalArgumentException e) { - throw new ParserException( - I18N.getText("macro.function.input.illegalArgumentType", imageString)); - } catch (IOException e1) { - throw new ParserException(I18N.getText("macro.function.html5.invalidURI", imageString)); - } - } else { - byte[] imageBytes = Base64.decode(imageString); - String imageCheck; - try { - imageCheck = new String(imageBytes, 0, 4); - } catch (Exception e) { - throw new ParserException(I18N.getText("dragdrop.unsupportedType", functionName)); - } - if (imageCheck.equals(FILE_HEADER_WEBP) - || imageCheck.equals(FILE_HEADER_JPG) - || imageCheck.equals(FILE_HEADER_PNG)) { - asset = Asset.createImageAsset(imageName, imageBytes); - } else { - throw new ParserException(I18N.getText("dragdrop.unsupportedType", functionName)); - } + } + if (uri != null + && uri.getScheme() != null + && isValidAssetScheme(uri) + && isValidAssetExtension(uri)) { + try { + URL url = uri.toURL(); + BufferedImage imageRAW = ImageIO.read(url); + asset = Asset.createImageAsset(imageName, imageRAW); + } catch (MalformedURLException | IllegalArgumentException e) { + throw new ParserException( + I18N.getText("macro.function.input.illegalArgumentType", imageString)); + } catch (IOException e) { + throw new ParserException( + I18N.getText("macro.function.html5.invalidURI", imageString)); } - AssetManager.putAsset(asset); - return "asset://" + asset.getMD5Key().toString(); } else { - throw new ParserException( - I18N.getText("macro.function.general.wrongParamType", functionName)); + byte[] imageBytes = isBase64Image + ? base64Bytes + : Base64.decode(imageString); + if (isPNG(imageBytes) || isJPG(imageBytes) || isWEBP(imageBytes)) { + asset = Asset.createImageAsset(imageName, imageBytes); + } else { + throw new ParserException( + I18N.getText("dragdrop.unsupportedType", functionName)); + } } + AssetManager.putAsset(asset); + return "asset://" + asset.getMD5Key().toString(); } /* getImage, getTokenImage, getTokenPortrait, or getTokenHandout */ @@ -388,10 +423,13 @@ private static Token findImageToken(final String name, String functionName) { * @param uri The URI to check. * @return {@code true} if the scheme is valid. */ - private boolean isValidAssetScheme(URI uri) { + public boolean isValidAssetScheme(URI uri) { + if (uri == null || uri.getScheme() == null) { + return false; + } return uri.getScheme().equalsIgnoreCase("http") - || uri.getScheme().equalsIgnoreCase("https") - || uri.getScheme().equalsIgnoreCase("lib"); + || uri.getScheme().equalsIgnoreCase("https") + || uri.getScheme().equalsIgnoreCase("asset"); } /** From eaddf531f1a5f015e1f668868dc9e59fc633d79c Mon Sep 17 00:00:00 2001 From: Jmr3366 Date: Sat, 3 Jan 2026 08:08:38 -0500 Subject: [PATCH 2/4] createAsset improvements in error checking and capabilities. Resolves #5917 --- .../maptool/client/functions/TokenImage.java | 55 ++++++++----------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/functions/TokenImage.java b/src/main/java/net/rptools/maptool/client/functions/TokenImage.java index 5e6058bfdf..a98087bc7d 100644 --- a/src/main/java/net/rptools/maptool/client/functions/TokenImage.java +++ b/src/main/java/net/rptools/maptool/client/functions/TokenImage.java @@ -82,32 +82,27 @@ private TokenImage() { "getTokenOpacity", "createAsset"); } + private static boolean isPNG(byte[] b) { - return b.length >= 8 - && (b[0] & 0xFF) == 0x89 - && b[1] == 0x50 - && b[2] == 0x4E - && b[3] == 0x47; + return b.length >= 8 && (b[0] & 0xFF) == 0x89 && b[1] == 0x50 && b[2] == 0x4E && b[3] == 0x47; } private static boolean isJPG(byte[] b) { - return b.length >= 3 - && (b[0] & 0xFF) == 0xFF - && (b[1] & 0xFF) == 0xD8 - && (b[2] & 0xFF) == 0xFF; + return b.length >= 3 && (b[0] & 0xFF) == 0xFF && (b[1] & 0xFF) == 0xD8 && (b[2] & 0xFF) == 0xFF; } private static boolean isWEBP(byte[] b) { return b.length >= 12 - && b[0] == 'R' - && b[1] == 'I' - && b[2] == 'F' - && b[3] == 'F' - && b[8] == 'W' - && b[9] == 'E' - && b[10] == 'B' - && b[11] == 'P'; + && b[0] == 'R' + && b[1] == 'I' + && b[2] == 'F' + && b[3] == 'F' + && b[8] == 'W' + && b[9] == 'E' + && b[10] == 'B' + && b[11] == 'P'; } + /** * Gets the TokenImage instance. * @@ -213,11 +208,11 @@ public Object childEvaluate( String imageString = args.get(1).toString(); if (imageName.isEmpty() || imageString.isEmpty()) { throw new ParserException( - I18N.getText("macro.function.general.paramCannotBeEmpty", functionName)); + I18N.getText("macro.function.general.paramCannotBeEmpty", functionName)); } if (imageString.length() <= 8) { throw new ParserException( - I18N.getText("macro.function.general.wrongParamType", functionName)); + I18N.getText("macro.function.general.wrongParamType", functionName)); } Asset asset; boolean isBase64Image = false; @@ -241,29 +236,25 @@ public Object childEvaluate( } } if (uri != null - && uri.getScheme() != null - && isValidAssetScheme(uri) - && isValidAssetExtension(uri)) { + && uri.getScheme() != null + && isValidAssetScheme(uri) + && isValidAssetExtension(uri)) { try { URL url = uri.toURL(); BufferedImage imageRAW = ImageIO.read(url); asset = Asset.createImageAsset(imageName, imageRAW); } catch (MalformedURLException | IllegalArgumentException e) { throw new ParserException( - I18N.getText("macro.function.input.illegalArgumentType", imageString)); + I18N.getText("macro.function.input.illegalArgumentType", imageString)); } catch (IOException e) { - throw new ParserException( - I18N.getText("macro.function.html5.invalidURI", imageString)); + throw new ParserException(I18N.getText("macro.function.html5.invalidURI", imageString)); } } else { - byte[] imageBytes = isBase64Image - ? base64Bytes - : Base64.decode(imageString); + byte[] imageBytes = isBase64Image ? base64Bytes : Base64.decode(imageString); if (isPNG(imageBytes) || isJPG(imageBytes) || isWEBP(imageBytes)) { asset = Asset.createImageAsset(imageName, imageBytes); } else { - throw new ParserException( - I18N.getText("dragdrop.unsupportedType", functionName)); + throw new ParserException(I18N.getText("dragdrop.unsupportedType", functionName)); } } AssetManager.putAsset(asset); @@ -428,8 +419,8 @@ public boolean isValidAssetScheme(URI uri) { return false; } return uri.getScheme().equalsIgnoreCase("http") - || uri.getScheme().equalsIgnoreCase("https") - || uri.getScheme().equalsIgnoreCase("asset"); + || uri.getScheme().equalsIgnoreCase("https") + || uri.getScheme().equalsIgnoreCase("asset"); } /** From 2e3384c97964ca42cb6f5196a9458a86238fc4a4 Mon Sep 17 00:00:00 2001 From: Jmr3366 Date: Mon, 9 Feb 2026 11:05:12 -0500 Subject: [PATCH 3/4] createAsset improvements in error checking and capabilities. Resolves #5917 Integrated usage of createAssetDetectType, removed file header byte checks --- .../maptool/client/functions/TokenImage.java | 94 ++++++------------- 1 file changed, 29 insertions(+), 65 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/functions/TokenImage.java b/src/main/java/net/rptools/maptool/client/functions/TokenImage.java index a98087bc7d..42538fa632 100644 --- a/src/main/java/net/rptools/maptool/client/functions/TokenImage.java +++ b/src/main/java/net/rptools/maptool/client/functions/TokenImage.java @@ -83,26 +83,6 @@ private TokenImage() { "createAsset"); } - private static boolean isPNG(byte[] b) { - return b.length >= 8 && (b[0] & 0xFF) == 0x89 && b[1] == 0x50 && b[2] == 0x4E && b[3] == 0x47; - } - - private static boolean isJPG(byte[] b) { - return b.length >= 3 && (b[0] & 0xFF) == 0xFF && (b[1] & 0xFF) == 0xD8 && (b[2] & 0xFF) == 0xFF; - } - - private static boolean isWEBP(byte[] b) { - return b.length >= 12 - && b[0] == 'R' - && b[1] == 'I' - && b[2] == 'F' - && b[3] == 'F' - && b[8] == 'W' - && b[9] == 'E' - && b[10] == 'B' - && b[11] == 'P'; - } - /** * Gets the TokenImage instance. * @@ -206,59 +186,42 @@ public Object childEvaluate( FunctionUtil.checkNumberParam(functionName, args, 2, 2); String imageName = args.get(0).toString(); String imageString = args.get(1).toString(); + Asset asset = null; if (imageName.isEmpty() || imageString.isEmpty()) { throw new ParserException( - I18N.getText("macro.function.general.paramCannotBeEmpty", functionName)); - } - if (imageString.length() <= 8) { - throw new ParserException( - I18N.getText("macro.function.general.wrongParamType", functionName)); - } - Asset asset; - boolean isBase64Image = false; - byte[] base64Bytes = null; - try { - base64Bytes = Base64.decode(imageString); - if (isPNG(base64Bytes) || isJPG(base64Bytes) || isWEBP(base64Bytes)) { - isBase64Image = true; - } - } catch (Exception ignore) { - } - URI uri = null; - if (!isBase64Image) { + I18N.getText("macro.function.general.paramCannotBeEmpty", functionName)); + } else if (imageString.length() > 8) { + byte[] imageBytes = Base64.decode(imageString); try { - uri = new URI(imageString); - if (uri.getScheme() == null) { + asset = Asset.createAssetDetectType(imageName,imageBytes); + } catch (Exception e) { + URI uri; + try { + uri = new URI(imageString); + } catch (URISyntaxException e1) { uri = null; } - } catch (URISyntaxException e) { - uri = null; + if (uri != null && isValidAssetScheme(uri) && isValidAssetExtension(uri)) { + try { + URL url = uri.toURL(); + BufferedImage imageRAW = ImageIO.read(url); + asset = Asset.createImageAsset(imageName, imageRAW); + } catch (MalformedURLException | IllegalArgumentException e2) { + throw new ParserException( + I18N.getText("macro.function.input.illegalArgumentType", imageString)); + } catch (IOException e3) { + throw new ParserException(I18N.getText("macro.function.html5.invalidURI", imageString)); + } + } } - } - if (uri != null - && uri.getScheme() != null - && isValidAssetScheme(uri) - && isValidAssetExtension(uri)) { - try { - URL url = uri.toURL(); - BufferedImage imageRAW = ImageIO.read(url); - asset = Asset.createImageAsset(imageName, imageRAW); - } catch (MalformedURLException | IllegalArgumentException e) { - throw new ParserException( - I18N.getText("macro.function.input.illegalArgumentType", imageString)); - } catch (IOException e) { - throw new ParserException(I18N.getText("macro.function.html5.invalidURI", imageString)); + if(asset!=null){ + AssetManager.putAsset(asset); + return "asset://" + asset.getMD5Key().toString(); } } else { - byte[] imageBytes = isBase64Image ? base64Bytes : Base64.decode(imageString); - if (isPNG(imageBytes) || isJPG(imageBytes) || isWEBP(imageBytes)) { - asset = Asset.createImageAsset(imageName, imageBytes); - } else { - throw new ParserException(I18N.getText("dragdrop.unsupportedType", functionName)); - } + throw new ParserException( + I18N.getText("macro.function.general.wrongParamType", functionName)); } - AssetManager.putAsset(asset); - return "asset://" + asset.getMD5Key().toString(); } /* getImage, getTokenImage, getTokenPortrait, or getTokenHandout */ @@ -420,7 +383,8 @@ public boolean isValidAssetScheme(URI uri) { } return uri.getScheme().equalsIgnoreCase("http") || uri.getScheme().equalsIgnoreCase("https") - || uri.getScheme().equalsIgnoreCase("asset"); + || uri.getScheme().equalsIgnoreCase("asset") + || uri.getScheme().equalsIgnoreCase("lib"); } /** From d82c6fc7813a9f06432420f1f6ec8b5f50aa98a9 Mon Sep 17 00:00:00 2001 From: Jmr3366 Date: Mon, 9 Feb 2026 12:46:35 -0500 Subject: [PATCH 4/4] spotless checks --- .../maptool/client/functions/TokenImage.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/functions/TokenImage.java b/src/main/java/net/rptools/maptool/client/functions/TokenImage.java index 42538fa632..0f351f64f2 100644 --- a/src/main/java/net/rptools/maptool/client/functions/TokenImage.java +++ b/src/main/java/net/rptools/maptool/client/functions/TokenImage.java @@ -189,11 +189,11 @@ public Object childEvaluate( Asset asset = null; if (imageName.isEmpty() || imageString.isEmpty()) { throw new ParserException( - I18N.getText("macro.function.general.paramCannotBeEmpty", functionName)); + I18N.getText("macro.function.general.paramCannotBeEmpty", functionName)); } else if (imageString.length() > 8) { byte[] imageBytes = Base64.decode(imageString); try { - asset = Asset.createAssetDetectType(imageName,imageBytes); + asset = Asset.createAssetDetectType(imageName, imageBytes); } catch (Exception e) { URI uri; try { @@ -208,19 +208,20 @@ public Object childEvaluate( asset = Asset.createImageAsset(imageName, imageRAW); } catch (MalformedURLException | IllegalArgumentException e2) { throw new ParserException( - I18N.getText("macro.function.input.illegalArgumentType", imageString)); + I18N.getText("macro.function.input.illegalArgumentType", imageString)); } catch (IOException e3) { - throw new ParserException(I18N.getText("macro.function.html5.invalidURI", imageString)); + throw new ParserException( + I18N.getText("macro.function.html5.invalidURI", imageString)); } } } - if(asset!=null){ + if (asset != null) { AssetManager.putAsset(asset); return "asset://" + asset.getMD5Key().toString(); } } else { throw new ParserException( - I18N.getText("macro.function.general.wrongParamType", functionName)); + I18N.getText("macro.function.general.wrongParamType", functionName)); } }