1 /* 2 * Copyright (c) 2012-2024 Hazendaz. 3 * 4 * All rights reserved. This program and the accompanying materials 5 * are made available under the terms of The Apache Software License, 6 * Version 2.0 which accompanies this distribution, and is available at 7 * http://www.apache.org/licenses/LICENSE-2.0.txt 8 * 9 * Contributors: 10 * Hazendaz (Jeremy Landis). 11 */ 12 package net.jangaroo.smartsprites.maven; 13 14 import java.io.File; 15 import java.io.IOException; 16 import java.util.ArrayList; 17 import java.util.List; 18 import java.util.Locale; 19 20 import org.apache.maven.plugin.AbstractMojo; 21 import org.apache.maven.plugin.MojoExecutionException; 22 import org.apache.maven.plugin.MojoFailureException; 23 import org.apache.maven.plugins.annotations.LifecyclePhase; 24 import org.apache.maven.plugins.annotations.Mojo; 25 import org.apache.maven.plugins.annotations.Parameter; 26 import org.carrot2.labs.smartsprites.SmartSpritesParameters; 27 import org.carrot2.labs.smartsprites.SpriteBuilder; 28 import org.carrot2.labs.smartsprites.message.Message; 29 import org.carrot2.labs.smartsprites.message.MessageLog; 30 import org.carrot2.labs.smartsprites.message.PrintStreamMessageSink; 31 32 /** 33 * Goal which creates Spritesheets from given css and image files Explanation of every variable is taken directly from 34 * the smartsprites documentation (https://www.w3schools.com/css/css_image_sprites.asp) 35 * <p> 36 * For further information on the use of smartsprites please refer to: 37 * https://www.w3schools.com/css/css_image_sprites.asp 38 */ 39 @Mojo(name = "smartsprites", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, requiresProject = false, threadSafe = true) 40 public class SmartSpritesMojo extends AbstractMojo { 41 42 /** The static root directory mode. */ 43 private static final String ROOT_DIR_MODE = "rootDirMode"; 44 45 /** The static css files mode. */ 46 private static final String CSS_FILES_MODE = "cssFilesMode"; 47 48 /** 49 * Directory in which SmartSprites processing should be done, required if css-files not specified or if 50 * output-dir-path specified, default: not specified. 51 * <p> 52 * SmartSprites will process all files with the *.css extension found in root-dir-path or any subdirectory of it. 53 * For more fine-grained control over the processed CSS files, see the css-files option. 54 * <p> 55 * If the provided root directory path is relative, it will be resolved against the current working directory. 56 */ 57 @Parameter(defaultValue = "${project.basedir}/src/main/sprites", property = "rootDirPath") 58 private File rootDirPath; 59 60 /** 61 * Paths of CSS files to process, required if root-dir-path not specified, default: not specified. 62 * <p> 63 * SmartSprites will process all CSS files listed using this option. If css-files is to be used together with 64 * output-dir-path, root-dir-path must also be specified so that SmartSprites can preserve the directory structure 65 * found in root-dir-path in output-dir-path. If root-dir-path and output-dir-path are used, css-files outside of 66 * root-dir-path will be ignored. 67 * <p> 68 * Relative CSS file paths provided using this option will be resolved against the current working directory. Please 69 * note that SmartSprites will not expand any wildcards (like style/*.css), it assumes the expansion is performed at 70 * the command line shell level. 71 * <p> 72 * To specify the list of CSS files to process in the SmartSprites Ant task, use one or more nested fileset 73 * elements. Please see the build.xml file in the distribution archive for an example. 74 */ 75 @Parameter(property = "cssFiles") 76 private List<File> cssFiles; 77 78 /** 79 * Output directory for processed CSS files and CSS-relative sprite images, optional, default: not specified. 80 * <p> 81 * If a non-empty output-dir-path is specified, a non-empty root-dir-path must also be provided. The directory 82 * structure relative to the root-dir-path will be preserved in the output directory. E.g. if CSS files are 83 * contained in the css/base directory of root-dir-path, the processed results will be written to 84 * output-dir-path/css/base. Also, CSS-relative sprite images will be written to the output directory. Sprite images 85 * with document-root-relative URLs will be written relative to the document-root-dir-path. If the output-dir-path 86 * directory does not exist, it will be created. If the provided output directory path is relative, it will be 87 * resolved against the current working directory. 88 * <p> 89 * You can leave the output-dir-path empty, in which case the CSS files will be written next to the original CSS 90 * files with the css-file-suffix, and sprite images will be written relative to the original CSS files. If you are 91 * using a non-empty output-dir-path, you might want to use an empty css-file-suffix. 92 */ 93 @Parameter(defaultValue = "${project.build.directory}/generated-resources/META-INF/resources/spritesheets", property = "outPutDirPath") 94 private File outputDirPath; 95 96 /** 97 * Document root path for document-root-relative (starting with /) image urls in CSS, optional, default: not 98 * specified. 99 * <p> 100 * All document-root-relative image and sprite URLs will be taken relative to document-root-dir-path. Also 101 * document-root-relative sprite URLs will be written relative to document-root-dir-path. You can leave this 102 * property empty if your CSS uses only CSS-relative image URLs. If the provided document root directory path is 103 * relative, it will be resolved against the current working directory. 104 */ 105 @Parameter(property = "documentRootDirPath") 106 private File documentRootDirPath; 107 108 /** 109 * Message logging level, optional, default: WARN. 110 * <p> 111 * Messages less important than log-level will not be shown. 112 * <p> 113 * SmartSprites has 5 levels of log messages (in the increasing order of importance). 114 * <p> 115 * INFO: information messages, can be safely ignored. 116 * <p> 117 * DEPRECATION: Notice messages related to deprecated features. 118 * <p> 119 * WARN: Warning messages, ignoring can lead to the converted designs looking broken. 120 * <p> 121 * ERROR: Error messages, SmartSprites cannot perform processing. 122 * <p> 123 * STATUS: Status messages displayed at the end of processing. 124 */ 125 @Parameter(defaultValue = "WARN", property = "logLevel") 126 private String logLevel; 127 128 /** 129 * Color depth of sprites in the PNG format, optional, default: AUTO. AUTO: PNG color depth will be chosen 130 * automatically. If the sprite image does not contain partial transparencies (alpha channel) and has less than 256 131 * colors, PNG8 will be used. Otherwise, the sprite will be saved in PNG24. DIRECT: PNG sprites will always be saved 132 * in the PNG24 format. INDEXED: PNG sprites will always be saved in the PNG8 format. If the sprite image contains 133 * partial transparencies (alpha channel) or has more than 255 colors, image quality loss may occur and appropriate 134 * warnings will be issued. See also the sprite-matte-color property. 135 */ 136 @Parameter(defaultValue = "AUTO", property = "spritePngDepth") 137 private String spritePngDepth; 138 139 /** 140 * The encoding to assume for input and output CSS files, default: UTF-8. For the list of allowed values, please see 141 * the list of encodings supported in Java. 142 */ 143 @Parameter(defaultValue = "UTF-8", property = "cssFileEncoding") 144 private String cssFileEncoding; 145 146 /** 147 * Suffix to be appended to the processed CSS file name, optional, default: . 148 */ 149 @Parameter(defaultValue = "", property = "cssFileSuffix") 150 private String cssFileSuffix; 151 152 /** 153 * To make sure the different modes are working correctly a mode has to be specified. Modes: - rootDirMode (rootDir 154 * is set, optionally outputdir! No css-Files!) - cssFilesMode (only cssFiles is set, no rootDir, no OutputDir) - 155 * cssFilesWithOutputDirMode (cssFiles are set, as well as rootDir and outputDir) 156 */ 157 @Parameter(defaultValue = SmartSpritesMojo.ROOT_DIR_MODE, property = "workingMode") 158 private String workingMode; 159 160 /** 161 * To enable skipping run of plugin. 162 */ 163 @Parameter(defaultValue = "false", alias = "skip", property = "skip") 164 private boolean skip; 165 166 @Override 167 public void execute() throws MojoExecutionException, MojoFailureException { 168 // Check if plugin run should be skipped 169 if (this.skip) { 170 this.getLog().info("Smartsprites is skipped"); 171 return; 172 } 173 174 // Check for the correct log-level 175 Message.MessageLevel msgLogLevel; 176 try { 177 msgLogLevel = Message.MessageLevel.valueOf(this.logLevel.toUpperCase(Locale.ENGLISH)); 178 } catch (final Exception e) { 179 throw new MojoExecutionException("LogLevel Error - please select a valid value! (INFO, WARN) ", e); 180 } 181 182 // Check for the correct PNG-Depth 183 SmartSpritesParameters.PngDepth pngDepth; 184 try { 185 pngDepth = SmartSpritesParameters.PngDepth.valueOf(this.spritePngDepth); 186 } catch (final Exception e) { 187 throw new MojoExecutionException("PngDepth Error - please select a valid value! (AUTO, DIRECT, INDEXED) ", 188 e); 189 } 190 191 // Make sure we are using only correct workingModes 192 if (!(this.workingMode.equals(SmartSpritesMojo.ROOT_DIR_MODE) 193 || this.workingMode.equals(SmartSpritesMojo.CSS_FILES_MODE) 194 || this.workingMode.equals("cssFilesWithOutputDirMode"))) { 195 throw new MojoExecutionException( 196 "workingMode Error - plese select a valid value! (rootDirMode, cssFilesMode, cssFilesWithOutputDirMode)"); 197 } 198 199 // Variables for paths 200 String rootDirPathTemp = ""; 201 String outputDirPathTemp = ""; 202 String documentRootDirPathTemp = ""; 203 List<String> cssFilesTemp = new ArrayList<>(); 204 205 // Check if we should set cssFiles to null, or if we are in a working mode 206 // where we want to use cssFiles 207 if (!this.workingMode.equals(SmartSpritesMojo.ROOT_DIR_MODE)) { 208 for (final File cssFile : this.cssFiles) { 209 if (cssFile.exists()) { 210 cssFilesTemp.add(cssFile.toString()); 211 } else { 212 throw new MojoExecutionException("The following css-file doesn't exist: " + cssFile); 213 } 214 } 215 } else { 216 this.cssFiles = null; 217 } 218 219 // Check if the folders exist or are at least configured 220 if (this.rootDirPath != null && !this.workingMode.equals(SmartSpritesMojo.CSS_FILES_MODE)) { 221 if (this.rootDirPath.exists()) { 222 rootDirPathTemp = this.rootDirPath.toString(); 223 } else { 224 throw new MojoExecutionException("The rootDirPath doesn't exist. " + this.rootDirPath.toString()); 225 } 226 } 227 228 // Check if the outputDirPath is set 229 if (this.outputDirPath != null && !this.workingMode.equals(SmartSpritesMojo.CSS_FILES_MODE)) { 230 outputDirPathTemp = this.outputDirPath.toString(); 231 } 232 233 // Check if the documentRootDirPath is set 234 if (this.documentRootDirPath != null) { 235 documentRootDirPathTemp = this.documentRootDirPath.toString(); 236 } 237 238 // Determine which workingMode we are in and check if all conditions for that 239 // specific mode are matched 240 if (this.workingMode.equals(SmartSpritesMojo.ROOT_DIR_MODE)) { 241 if (this.rootDirPath == null) { 242 throw new MojoExecutionException("Please configure a rootDirPath."); 243 } 244 cssFilesTemp = null; 245 } else if (this.workingMode.equals(SmartSpritesMojo.CSS_FILES_MODE)) { 246 if (this.cssFiles == null) { 247 throw new MojoExecutionException("Please configure some cssFiles."); 248 } 249 rootDirPathTemp = null; 250 outputDirPathTemp = null; 251 } else if ((this.workingMode.equals("cssFilesWithOutputDirMode") && this.cssFiles == null) 252 || this.rootDirPath == null || this.outputDirPath == null) { 253 throw new MojoExecutionException("Please configure cssFiles and/or a rootDirPath and/or an outputDirPath"); 254 } 255 256 // Configure the SmartSpritesParameters for execution 257 SmartSpritesParameters smartParameters = new SmartSpritesParameters(rootDirPathTemp, cssFilesTemp, 258 outputDirPathTemp, documentRootDirPathTemp, msgLogLevel, this.cssFileSuffix, pngDepth, 259 this.cssFileEncoding); 260 261 final MessageLog messageLog = new MessageLog( 262 new PrintStreamMessageSink(System.out, smartParameters.getLogLevel())); 263 SpriteBuilder spriteBuilder; 264 265 // Try to execute SmartSprites with the configured parameters and the defined messageLog 266 try { 267 spriteBuilder = new SpriteBuilder(smartParameters, messageLog); 268 spriteBuilder.buildSprites(); 269 } catch (final IOException e) { 270 throw new MojoExecutionException("Smartsprites error: ", e); 271 } 272 } 273 274 }