5

Per the Google Page Speed recommendations, I want to Specify image dimensions to "Optimize browser rendering."

Specifying a width and height for all images allows for faster rendering by eliminating the need for unnecessary reflows and repaints.

I am investigating ways to traverse through the image files (PNG, JPEG) in my static content project and output a file with the path and filename of each image file as well as the height and width in pixels. I would then use that to help me construct the tags by using the src attribute data to lookup the values to use for the height and width attributes.

\images\logo.png,100,25

My first ideas was looking for an ANT task, since our static content build uses Ant for other purposes (like using YUI Compressor on JavaScript and CSS files). I am open to other ideas as well, including other methods to solve this problem. I would prefer to not have to manually do this work.

Kevin Hakanson
  • 41,386
  • 23
  • 126
  • 155

3 Answers3

3

You could try this https://github.com/mattwildig/image-size-report-task, which I've made just for this question.

matt
  • 78,533
  • 8
  • 163
  • 197
  • I had this implemented, but didn't get this question updated before your answer. I'm marking your answer as accepted. Thanks. – Kevin Hakanson Nov 08 '10 at 18:55
1

Here was what I implemented so far (needs testing and clean up). Basically, used Tutorial: Tasks using Properties, Filesets & Paths to get me started in an Ant task and How to get image height and width using java? to extract the image dimensions. I'm going to compare against matt's answer before I deploy.

The test build script from my project:

<project name="ImagesTask" basedir="." default="test">
    <target name="init">
        <taskdef name="images" classname="ImageInfoTask" classpath="..\dist\ImageTask.jar"/>
    </target>
    <target name="test" depends="init">
        <images outputFile="data/images.xml">
            <fileset dir="data" includes="images/**/*.jpg"/>
            <fileset dir="data" includes="images/**/*.gif"/>
            <fileset dir="data" includes="images/**/*.png"/>
        </images>
    </target>
</project>

The Java source (without imports):

public class ImageInfoTask extends Task {

    private String outputFile;
    private List fileSetList = new ArrayList();
    private PrintStream outputFileStream;

    public void setOutputFile(String outputFile) {
        this.outputFile = outputFile.replace("/", File.separator);
    }

    public void addFileset(FileSet fileset) {
        fileSetList.add(fileset);
    }

    protected void validate() {
        if (outputFile == null) {
            throw new BuildException("file not set");
        }

        if (fileSetList.size() < 1) {
            throw new BuildException("fileset not set");
        }
    }

    protected void openOutputFile() throws IOException {
        FileOutputStream out = new FileOutputStream(this.outputFile);

        // Connect print stream to the output stream
        this.outputFileStream = new PrintStream(out, true, "UTF-8");

        this.outputFileStream.println("<images>");
    }

    protected void writeImgToOutputFile(String filename, Dimension dim) {
        String imgTag = "  <img src=\"/" + filename.replace("\\", "/")
                + "\" height=\"" + dim.height + "\" width=\"" + dim.width
                + "\" />";

        this.outputFileStream.println(imgTag);
    }

    protected void closeOutputFile() {
        this.outputFileStream.println("</images>");

        this.outputFileStream.close();
    }

    @Override
    public void execute() {
        validate();

        try {
            openOutputFile();

            for (Iterator itFSets = fileSetList.iterator(); itFSets.hasNext();) {
                FileSet fs = (FileSet) itFSets.next();
                DirectoryScanner ds = fs.getDirectoryScanner(getProject());
                String[] includedFiles = ds.getIncludedFiles();
                for (int i = 0; i < includedFiles.length; i++) {
                    String filename = includedFiles[i];

                    Dimension dim = getImageDim(ds.getBasedir() + File.separator + filename);
                    if (dim != null) {
                        writeImgToOutputFile(filename, dim);
                    }
                }
            }

            closeOutputFile();
        }  catch (IOException ex) {
            log(ex.getMessage());
        }
    }

    private Dimension getImageDim(final String path) {
        Dimension result = null;
        String suffix = this.getFileSuffix(path);
        Iterator<ImageReader> iter = ImageIO.getImageReadersBySuffix(suffix);
        if (iter.hasNext()) {
            ImageReader reader = iter.next();
            try {
                ImageInputStream stream = new FileImageInputStream(new File(path));
                reader.setInput(stream);
                int width = reader.getWidth(reader.getMinIndex());
                int height = reader.getHeight(reader.getMinIndex());
                result = new Dimension(width, height);
            } catch (IOException e) {
                log(path + ": " + e.getMessage());
            } finally {
                reader.dispose();
            }
        }
        return result;
    }

    private String getFileSuffix(final String path) {
        String result = null;
        if (path != null) {
            result = "";
            if (path.lastIndexOf('.') != -1) {
                result = path.substring(path.lastIndexOf('.'));
                if (result.startsWith(".")) {
                    result = result.substring(1);
                }
            }
        }
        return result;
    }
}
Community
  • 1
  • 1
Kevin Hakanson
  • 41,386
  • 23
  • 126
  • 155
  • I used the same ant tutorial to figure out how to use filesets, so I think both implementations are pretty similiar on that side. I didn't know much about ImageIO, so I just read the file into a BufferedImage and got the dimensions from that. That's why I needed to set java.awt.headless - I was getting a java menu bar flash up when running the script. I suspect the 'pure' ImageIO approach you've used will likely be a fair bit faster than my version, even if the code's a bit more verbose. Anyway - it was an interesting little project for a Sunday evening. – matt Nov 08 '10 at 19:49
0

I'm not aware of such ant task readily available but it should be relatively simple to write one. In PNG format image size is stored right at the beginning of the file in IHDR header. There are numerous samples of PNG parsers on Google - for example this. Wrap it up in ant task and you're done.

maximdim
  • 8,041
  • 3
  • 33
  • 48