All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.oneandone.maven.plugins.spritepacker.SpritePacker Maven / Gradle / Ivy

package net.oneandone.maven.plugins.spritepacker;

import net.oneandone.maven.plugins.spritepacker.converters.CssPackingConverter;
import net.oneandone.maven.plugins.spritepacker.converters.JsonPackingConverter;
import net.oneandone.maven.plugins.spritepacker.converters.LessPackingConverter;
import net.oneandone.maven.plugins.spritepacker.converters.PackingConverter;
import net.oneandone.maven.plugins.spritepacker.converters.SpritesheetPackingConverter;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.Scanner;
import org.sonatype.plexus.build.incremental.BuildContext;

import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


/**
 * Packs spritesheets from supplied images.
 *
 * @author Robert Murphy, mklein, ssiegler
 */
@Mojo(name = "compile", defaultPhase = LifecyclePhase.PROCESS_SOURCES)
public class SpritePacker extends AbstractMojo {

    /**
     * Output spritesheet image file
     */
    @Parameter(required = true)
    File output;

    /**
     * Optional output JSON(P) description file containing coordinates and dimensions
     */
    @Parameter
    File json;

    /**
     * Optional variable for JSONP files
     * e.g.
     * { image: {...} }
     * becomes
     * jsonpVar = { image: {...} }
     */
    @Parameter
    String jsonpVar;

    /**
     * Optional output CSS file containing coordinates and dimensions, where each icon is saved as its own class.
     */
    @Parameter
    File css;

    /**
     * Optional CSS class prefix. Default value is "icon".
     */
    @Parameter
    String cssPrefix;

    /**
     * Optional output Less file containing coordinates and dimensions, where each icon is saved as position and size mixins.
     */
    @Parameter
    File less;

    /**
     * Optional Less namespace name.
     */
    @Parameter
    String lessNamespace;

    /**
     * The source directory containing the icons
     */
    @Parameter(required = true)
    File sourceDirectory;

    /**
     * List of files to include. Specified as fileset patterns which are relative to the source directory. Default is all files.
     */
    @Parameter
    String[] includes = new String[] { "**/*" };

    /**
     * List of files to exclude. Specified as fileset patterns which are relative to the source directory.
     */
    @Parameter
    String[] excludes = new String[] { };

    /**
     * Optional transparent padding added between images in spritesheet.
     */
    @Parameter(defaultValue = "0")
    Integer padding;

    /**
     * Optionally force the sprite packer to always re-generate files regardless of whether new graphics were found.
     */
    @Parameter(defaultValue = "false")
    Boolean forceOverwrite;

    /**
     * Optionally skip the execution of the plugin.
     */
    @Parameter(defaultValue = "false")
    Boolean skip;

    @Component
    BuildContext buildContext;

    /**
     * Execute the MOJO.
     *
     * @throws MojoExecutionException if something unexpected occurs.
     */
    public void execute() throws MojoExecutionException {

        if (skip) {
            log("Execution of spritepacker was skipped.");
            return;
        }

        long startTime = System.currentTimeMillis();

        List inputs = scanPaths(sourceDirectory, includes, excludes);

        // Check if there are actually any inputs to do anything with
        if (inputs.isEmpty()) {
            log("No source images found.");
            return;
        }

        Path outputPath = fileToPath(output);
        Path jsonPath = fileToPath(json);
        Path cssPath = fileToPath(css);
        Path lessPath = fileToPath(less);

        // Load output files into an ArrayList
        List outputs = Arrays.asList(outputPath, jsonPath, cssPath, lessPath);

        // If force overwrite not specified, and the JSON file is not being created for the first time,
        // and the output files were modified more recently than the input files, return.
        try {
            if (!forceOverwrite && !Utils.shouldWriteOutput(inputs, outputs)) {
                log("No source images modified.");
                return;
            }
        } catch (IOException e) {
            throw new MojoExecutionException("Could not check if output should be written.", e);
        }

        log("Loading " + inputs.size() + " images from " + sourceDirectory.getAbsolutePath());

        // Load images defined in input array
        List images = loadImages(inputs);

        log("Packing images...");

        // Add packing information
        ImagePacking imagePacking = packImages(images);

        List converters = Arrays.asList(new SpritesheetPackingConverter(outputPath),
                                                          new JsonPackingConverter(jsonPath, jsonpVar),
                                                          new CssPackingConverter(cssPath, cssPrefix),
                                                          new LessPackingConverter(lessPath, lessNamespace));

        for (PackingConverter converter : converters) {
            executeConverter(images, imagePacking, converter);
        }

        long took = System.currentTimeMillis() - startTime;
        log("Done - took " + took + "ms!");

    }

    // Allow tests to stub or verify the packing
    protected ImagePacking packImages(List images) {
        return PackGrowing.fit(images, padding);
    }

    // Allow tests to stub or verify converter execution
    protected void executeConverter(List images, ImagePacking imagePacking, PackingConverter converter) throws MojoExecutionException {
        converter.convert(images, imagePacking, getLog());
    }

    /**
     * Convert a file to a path, or return null if the file is null
     *
     * @param file the file to convert to a path
     * @return the path that was converted from a file
     */
    protected Path fileToPath(File file) {
        return (file == null) ? null : file.toPath();
    }

    /**
     * Create a list of files within a source directory, including subdirectories,
     * that match the includes and excludes criteria
     *
     * @param sourceDirectory the source directory
     * @param includes        criterion for files to include
     * @param excludes        criterion for files to exclude
     * @return list of matching files
     */
    protected List scanPaths(File sourceDirectory, String[] includes, String[] excludes) {
        Scanner scanner = buildContext.newScanner(sourceDirectory, true);
        scanner.setIncludes(includes);
        scanner.setExcludes(excludes);
        scanner.scan();
        String[] fileNames = scanner.getIncludedFiles();

        // sort files by path and name
        Arrays.sort(fileNames);

        List paths = new ArrayList<>(fileNames.length);
        for (String fileName : fileNames) {
            paths.add(sourceDirectory.toPath().resolve(fileName));
        }
        return paths;
    }

    /**
     * Load list of image files as a list of NamedImages
     *
     * @param imageFiles the image files to load
     * @return the list of loaded NamedImages
     * @throws MojoExecutionException
     */
    protected List loadImages(List imageFiles) throws MojoExecutionException {
        List images = new ArrayList<>(imageFiles.size());
        for (Path f : imageFiles) {
            try {
                String basename = FileUtils.removeExtension(f.getFileName().toString());
                images.add(new NamedImage(ImageIO.read(Files.newInputStream(f)), basename));
            } catch (IOException e) {
                throw new MojoExecutionException("Failed to open file: " + f.toAbsolutePath(), e);
            }
        }
        return images;
    }

    public void log(Object message) {
        getLog().info(message.toString());
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy