Skip to content
This repository has been archived by the owner on Nov 16, 2018. It is now read-only.

Commit

Permalink
CSSEmbedTask.java
Browse files Browse the repository at this point in the history
- Added native Ant task for running CSSEmbed, allows for much faster builds
- Replicates some of the functionality of CSSEmbed.java but in such a different style I didn't think reconciling them would necessarily be possible.

CSSURLEmbedder.java
- Count conversions correctly (uri -> datauri wasn't incrementing conversions counter)
- Clean up some formatting around opening curly braces & "if ()" blocks
- Wrap total conversion [INFO] line in a verbosity check so it doesn't always output
  • Loading branch information
tivac committed Apr 29, 2011
1 parent 62288c7 commit 3df7dfc
Show file tree
Hide file tree
Showing 2 changed files with 252 additions and 13 deletions.
240 changes: 240 additions & 0 deletions src/net/nczonline/web/cssembed/CSSEmbedTask.java
@@ -0,0 +1,240 @@
/*
* Copyright (c) 2009 Nicholas C. Zakas. All rights reserved.
* http://www.nczonline.net/
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package net.nczonline.web.cssembed;

import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.BuildException;

import org.apache.tools.ant.types.Mapper;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.ResourceCollection;
import org.apache.tools.ant.types.resources.FileProvider;
import org.apache.tools.ant.types.resources.FileResource;

import org.apache.tools.ant.util.FileNameMapper;
import org.apache.tools.ant.util.IdentityMapper;

import java.io.*;

import java.util.Vector;
import java.util.Iterator;

//Define a custom Ant Task that calls into the CSS Embedder
public class CSSEmbedTask extends Task {

//attribute options
private String charset = "UTF-8";
private String root;
private boolean mhtml;
private String mhtmlRoot;
private boolean skipMissing;
private boolean verbose = false;
private int maxUriLength = 0;
private int maxImageSize = 0;
private File srcFile;
private File destFile;

//support nested resource collections & mappers
private Mapper mapperElement = null;
private Vector rcs = new Vector();

//Simple Setters
public void setCharset(String charset) {
this.charset = charset;
}

public void setRoot(String root) {
this.root = root;
}

public void setMhtml(boolean mhtml) {
this.mhtml = mhtml;
}

public void setMhtmlRoot(String mhtmlRoot) {
this.mhtmlRoot = mhtmlRoot;
}

public void setSkipMissing(boolean skipMissing) {
this.skipMissing = skipMissing;
}

public void setVerbose(boolean verbose) {
this.verbose = verbose;
}

public void setMaxUriLength(int maxUriLength) {
this.maxUriLength = maxUriLength;
}

public void setMaxImageSize(int maxImageSize) {
this.maxImageSize = maxImageSize;
}

public void setSrcFile(File srcFile) {
this.srcFile = srcFile;
}

public void setDestFile(File destFile) {
this.destFile = destFile;
}

//More complicated setters for nested elements...

//add a collection of resources to copy
public void add(ResourceCollection res) {
rcs.add(res);
}

//mapper takes source files & converts them to dest files
public Mapper createMapper() throws BuildException {
if (mapperElement != null) {
throw new BuildException("Cannot define more than one mapper", getLocation());
}
mapperElement = new Mapper(getProject());
return mapperElement;
}

//support multiple types of filename mappers being added
public void add(FileNameMapper fileNameMapper) {
createMapper().add(fileNameMapper);
}

//returns the mapper to use based on nested elements, defaults to IdentityMapper
private FileNameMapper getMapper() {
FileNameMapper mapper = null;
if (mapperElement != null) {
mapper = mapperElement.getImplementation();
} else {
mapper = new IdentityMapper();
}
return mapper;
}

//ensure that attributes are legit
protected void validateAttributes() throws BuildException {
//if there's no nested resource containers make sure that a srcFile/destFile are set
if(this.rcs == null || this.rcs.size() == 0) {
if (this.srcFile == null || !this.srcFile.exists()) {
throw new BuildException("Must specify an input file or at least one nested resource", getLocation());
}

if(this.destFile == null) {
throw new BuildException("Must specify an output file or at least one nested resource", getLocation());
}
}

if(this.mhtml && this.mhtmlRoot == null) {
throw new BuildException("Must specify mhtmlRoot in mhtml mode", getLocation());
}

if(this.mhtmlRoot != null && !this.mhtml) {
log("mhtmlRoot has no effect if mhtml mode is not activated", Project.MSG_WARN);
}
}

//run the task
public void execute () throws BuildException {
validateAttributes();

//set options flags
int options = (this.mhtml) ? CSSURLEmbedder.MHTML_OPTION : CSSURLEmbedder.DATAURI_OPTION;
if(skipMissing) {
options = options | CSSURLEmbedder.SKIP_MISSING_OPTION;
}

if(srcFile != null && srcFile.exists()) {
try {
embed(srcFile, destFile, options);
} catch(IOException ex) {
throw new BuildException(ex.getMessage(), ex);
}
}

FileNameMapper mapper = getMapper();

for(Iterator it = this.rcs.iterator(); it.hasNext();) {
ResourceCollection rc = (ResourceCollection) it.next();

for(Iterator rcit = rc.iterator(); rcit.hasNext();) {
FileResource fr = (FileResource) rcit.next();
File in = fr.getFile();

String[] mapped = mapper.mapFileName(in.getName());
if (mapped != null && mapped.length > 0) {
for(int k = 0; k < mapped.length; k++) {
File out = getProject().resolveFile(in.getParent() + File.separator + mapped[k]);

try {
embed(in, out, options);
} catch(IOException ex) {
throw new BuildException(ex.getMessage(), ex);
}
}
}
}
}
}

private void embed(File input, File output, int options) throws IOException {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
Reader in = new InputStreamReader(new FileInputStream(input), charset);
Writer out = new OutputStreamWriter(bytes, charset);
String pathRoot = root;

if(pathRoot == null) {
pathRoot = input.getCanonicalPath();
pathRoot = pathRoot.substring(0, pathRoot.lastIndexOf(File.separator));
}

if (!pathRoot.endsWith(File.separator)){
pathRoot += File.separator;
}

if(verbose) {
log("[INFO] embedding images from '" + input + "'");
}

CSSURLEmbedder embedder = new CSSURLEmbedder(in, options, verbose, maxUriLength, maxImageSize);

if(mhtml) {
embedder.setMHTMLRoot(mhtmlRoot);
embedder.setFilename(output.getName());
}

embedder.embedImages(out, pathRoot);

in.close();
out.close();

if(bytes.size() > 0) {
if(verbose) {
log("[INFO] Writing to file: " + output);
}

bytes.writeTo(new FileOutputStream(output));
}
}
}
25 changes: 12 additions & 13 deletions src/net/nczonline/web/cssembed/CSSURLEmbedder.java
Expand Up @@ -215,7 +215,6 @@ public void embedImages(Writer out, String root) throws IOException {
}
foundMedia.put(url, lineNum);


//Begin processing URL
String newUrl = url;
if (verbose){
Expand All @@ -230,18 +229,16 @@ public void embedImages(Writer out, String root) throws IOException {

//get the data URI format
String uriString = getImageURIString(newUrl, url);

//if it doesn't begin with data:, it's not a data URI
if (uriString.startsWith("data:")){


if (maxUriLength > 0 && uriString.length() > maxUriLength) {
if (maxUriLength > 0 && uriString.length() > maxUriLength){
if (verbose){
System.err.println("[WARNING] File " + newUrl + " creates a data URI larger than " + maxUriLength + " bytes. Skipping.");
}
builder.append(url);
} else if (maxUriLength > 0 && uriString.length() > maxUriLength){
if (verbose) {
if (verbose){
System.err.println("[INFO] File " + newUrl + " creates a data URI longer than " + maxUriLength + " characters. Skipping.");
}
builder.append(url);
Expand All @@ -252,7 +249,6 @@ public void embedImages(Writer out, String root) throws IOException {
* have both a data URI and MHTML in the same file.
*/
if (hasOption(MHTML_OPTION)){

String entryName = getFilename(url);

//create MHTML header entry
Expand All @@ -272,6 +268,7 @@ public void embedImages(Writer out, String root) throws IOException {
conversions++;
} else if (hasOption(DATAURI_OPTION)){
builder.append(uriString);
conversions++;
}
}
} else {
Expand Down Expand Up @@ -306,8 +303,10 @@ public void embedImages(Writer out, String root) throws IOException {
mhtmlHeader.append("*/\n");
out.write(mhtmlHeader.toString());
}

System.err.println("[INFO] Converted " + conversions + " images to data URIs.");

if (verbose){
System.err.println("[INFO] Converted " + conversions + " images to data URIs.");
}

out.write(builder.toString());
}
Expand Down Expand Up @@ -353,9 +352,9 @@ private String getImageURIString(String url, String originalUrl) throws IOExcept
}

//check file size if we've been asked to
if(this.maxImageSize > 0 && file.length() > this.maxImageSize) {
if(verbose) {
System.err.println("[INFO] File " + originalUrl + " is larger than " + this.maxImageSize + " bytes. Skipping.");
if (maxImageSize > 0 && file.length() > maxImageSize){
if (verbose){
System.err.println("[INFO] File '" + originalUrl + "' is larger than " + maxImageSize + " bytes. Skipping.");
}

writer.write(originalUrl);
Expand All @@ -369,7 +368,7 @@ private String getImageURIString(String url, String originalUrl) throws IOExcept
System.err.println("[INFO] Generated data URI for '" + url + "'.");
}
} catch (FileNotFoundException e){
if(hasOption(SKIP_MISSING_OPTION)) {
if (hasOption(SKIP_MISSING_OPTION)){
System.err.println("[INFO] Could not find file. " + e.getMessage() + " Skipping.");

writer.write(originalUrl);
Expand Down

0 comments on commit 3df7dfc

Please sign in to comment.