package ar.com.hjg.pngj.chunks; // see http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html // http://www.w3.org/TR/PNG/#5Chunk-naming-conventions // http://www.w3.org/TR/PNG/#table53 import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; import ar.com.hjg.pngj.PngHelperInternal; import ar.com.hjg.pngj.PngjException; /** * Helper methods and constants related to Chunk processing. *
* This should only be of interest to developers doing special chunk processing
* or extending the ChunkFactory
*/
public class ChunkHelper {
ChunkHelper() {}
public static final String IHDR = "IHDR";
public static final String PLTE = "PLTE";
public static final String IDAT = "IDAT";
public static final String IEND = "IEND";
public static final String cHRM = "cHRM";
public static final String gAMA = "gAMA";
public static final String iCCP = "iCCP";
public static final String sBIT = "sBIT";
public static final String sRGB = "sRGB";
public static final String bKGD = "bKGD";
public static final String hIST = "hIST";
public static final String tRNS = "tRNS";
public static final String pHYs = "pHYs";
public static final String sPLT = "sPLT";
public static final String tIME = "tIME";
public static final String iTXt = "iTXt";
public static final String tEXt = "tEXt";
public static final String zTXt = "zTXt";
public static final byte[] b_IHDR = toBytes(IHDR);
public static final byte[] b_PLTE = toBytes(PLTE);
public static final byte[] b_IDAT = toBytes(IDAT);
public static final byte[] b_IEND = toBytes(IEND);
/*
* static auxiliary buffer. any method that uses this should synchronize against this
*/
private static byte[] tmpbuffer = new byte[4096];
/**
* Converts to bytes using Latin1 (ISO-8859-1)
*/
public static byte[] toBytes(String x) {
try {
return x.getBytes(PngHelperInternal.charsetLatin1name);
} catch (UnsupportedEncodingException e) {
throw new PngBadCharsetException(e);
}
}
/**
* Converts to String using Latin1 (ISO-8859-1)
*/
public static String toString(byte[] x) {
try {
return new String(x, PngHelperInternal.charsetLatin1name);
} catch (UnsupportedEncodingException e) {
throw new PngBadCharsetException(e);
}
}
/**
* Converts to String using Latin1 (ISO-8859-1)
*/
public static String toString(byte[] x, int offset, int len) {
try {
return new String(x, offset, len, PngHelperInternal.charsetLatin1name);
} catch (UnsupportedEncodingException e) {
throw new PngBadCharsetException(e);
}
}
/**
* Converts to bytes using UTF-8
*/
public static byte[] toBytesUTF8(String x) {
try {
return x.getBytes(PngHelperInternal.charsetUTF8name);
} catch (UnsupportedEncodingException e) {
throw new PngBadCharsetException(e);
}
}
/**
* Converts to string using UTF-8
*/
public static String toStringUTF8(byte[] x) {
try {
return new String(x, PngHelperInternal.charsetUTF8name);
} catch (UnsupportedEncodingException e) {
throw new PngBadCharsetException(e);
}
}
/**
* Converts to string using UTF-8
*/
public static String toStringUTF8(byte[] x, int offset, int len) {
try {
return new String(x, offset, len, PngHelperInternal.charsetUTF8name);
} catch (UnsupportedEncodingException e) {
throw new PngBadCharsetException(e);
}
}
/**
* critical chunk : first letter is uppercase
*/
public static boolean isCritical(String id) {
return (Character.isUpperCase(id.charAt(0)));
}
/**
* public chunk: second letter is uppercase
*/
public static boolean isPublic(String id) { //
return (Character.isUpperCase(id.charAt(1)));
}
/**
* Safe to copy chunk: fourth letter is lower case
*/
public static boolean isSafeToCopy(String id) {
return (!Character.isUpperCase(id.charAt(3)));
}
/**
* "Unknown" just means that our chunk factory (even when it has been
* augmented by client code) did not recognize its
* id
*/
public static boolean isUnknown(PngChunk c) {
return c instanceof PngChunkUNKNOWN;
}
/**
* Finds position of null byte in array
*
* @param b
* @return -1 if not found
*/
public static int posNullByte(byte[] b) {
for (int i = 0; i < b.length; i++)
if (b[i] == 0)
return i;
return -1;
}
/**
* Decides if a chunk should be loaded, according to a ChunkLoadBehaviour
*
* @param id
* @param behav
* @return true/false
*/
public static boolean shouldLoad(String id, ChunkLoadBehaviour behav) {
if (isCritical(id))
return true;
switch (behav) {
case LOAD_CHUNK_ALWAYS:
return true;
case LOAD_CHUNK_IF_SAFE:
return isSafeToCopy(id);
case LOAD_CHUNK_NEVER:
return false;
case LOAD_CHUNK_MOST_IMPORTANT:
return id.equals(PngChunkTRNS.ID);
}
return false; // should not reach here
}
public final static byte[] compressBytes(byte[] ori, boolean compress) {
return compressBytes(ori, 0, ori.length, compress);
}
public static byte[] compressBytes(byte[] ori, int offset, int len, boolean compress) {
try {
ByteArrayInputStream inb = new ByteArrayInputStream(ori, offset, len);
InputStream in = compress ? inb : new InflaterInputStream(inb);
ByteArrayOutputStream outb = new ByteArrayOutputStream();
OutputStream out = compress ? new DeflaterOutputStream(outb) : outb;
shovelInToOut(in, out);
in.close();
out.close();
return outb.toByteArray();
} catch (Exception e) {
throw new PngjException(e);
}
}
/**
* Shovels all data from an input stream to an output stream.
*/
private static void shovelInToOut(InputStream in, OutputStream out) throws IOException {
synchronized (tmpbuffer) {
int len;
while ((len = in.read(tmpbuffer)) > 0) {
out.write(tmpbuffer, 0, len);
}
}
}
/**
* Returns only the chunks that "match" the predicate
*
* See also trimList()
*/
public static List