001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.util;
011:
012: import java.io.*;
013:
014: /**
015: * IECompatibleJpegInputStream removes additional information left by PhotoShop 7 in jpegs
016: * , this information may crash Internet Exploder. that's why you need to remove it.
017: *
018: * With PS 7, Adobe decided by default to embed XML-encoded "preview" data into JPEG files,
019: * using a feature of the JPEG format that permits embedding of arbitrarily-named "profiles".
020: * In theory, these files are valid according to the JPEG specifications.
021: * However they break many applications, including Quark and, significantly,
022: * various versions of Internet Explorer on various platforms.
023: *
024: * @since MMBase 1.7
025: * @author Kees Jongenburger <keesj@dds.nl>
026: * @version $Id: IECompatibleJpegInputStream.java,v 1.8 2006/06/27 14:36:38 johannes Exp $
027: */
028: public class IECompatibleJpegInputStream extends FilterInputStream
029: implements Runnable {
030:
031: private PipedInputStream pis;
032: private PipedOutputStream pos;
033:
034: /**
035: * create a new InputStream that parse the content of a jpeg file and removes application headers
036: * if the content is not a jpeg the content remains unaffected
037: */
038: public IECompatibleJpegInputStream(InputStream in) {
039: super (in);
040: pis = new PipedInputStream();
041: pos = new PipedOutputStream();
042: try {
043: pis.connect(pos);
044: } catch (IOException ioe) {
045: }
046: ThreadPools.filterExecutor.execute(this );
047: }
048:
049: public void run() {
050: try {
051: //read the first 2 byte so see if it is a jpeg file
052: int magic1 = in.read();
053: int magic2 = in.read();
054: pos.write(magic1);
055: pos.write(magic2);
056:
057: if (magic1 == 0xff && magic2 == 0xd8) {
058: int b;
059: //start reading
060: while ((b = in.read()) != -1) {
061: if (b == 0xff) {
062: int marker = in.read();
063:
064: if (marker == 0x00 || marker == 0xd8
065: || marker == 0xd9) { //some markers have no "size" like the escaping .. start and end of jpeg
066: pos.write(b);
067: pos.write(marker);
068: } else if (marker >= 0xe0 && marker <= 0xef) { //application markers not really required
069: //} else if (marker == 0xed) { //this marker is an application marker used by photoshop. it looks like this is the marker
070: //where photoshop stores the xml stuff that we never wanted so if you only want to remove the xml part of the file this is enough
071: int msb = in.read();
072: int lsb = in.read();
073:
074: int size = msb * 256 + lsb;
075: in.skip(size - 2);
076: } else {
077: int msb = in.read();
078:
079: int lsb = in.read();
080:
081: int size = msb * 256 + lsb;
082: size -= 2;
083: pos.write(b);
084: pos.write(marker);
085: pos.write(msb);
086: pos.write(lsb);
087: while (size > 0) {
088: pos.write(in.read());
089: size--;
090: }
091: }
092: } else {
093: pos.write(b);
094: }
095: }
096: } else {
097: int c = 0;
098: byte[] buf = new byte[1024];
099: while ((c = in.read(buf)) != -1) {
100: pos.write(buf, 0, c);
101: }
102: }
103: in.close();
104: pos.flush();
105: pos.close();
106: } catch (Exception e) {
107: }
108: ;
109: }
110:
111: public int available() throws IOException {
112: return pis.available();
113: }
114:
115: public void close() throws IOException {
116: pis.close();
117: super .close();
118: }
119:
120: public int read() throws IOException {
121: return pis.read();
122: }
123:
124: public int read(byte[] b, int off, int len) throws IOException {
125: return pis.read(b, off, len);
126: }
127:
128: public int read(byte[] b) throws IOException {
129: return pis.read(b);
130: }
131:
132: public long skip(long n) throws IOException {
133: return pis.skip(n);
134: }
135:
136: /**
137: * Util method that uses the IECompatibleInputStream to convert a byte array
138: * if the content is not a jpeg the content is not affected
139: * @param in the byte array
140: * @return the converted (ie compatible) jpeg
141: */
142: public static byte[] process(byte[] in) {
143: try {
144: InputStream inputStream = new IECompatibleJpegInputStream(
145: new ByteArrayInputStream(in));
146: ByteArrayOutputStream out = new ByteArrayOutputStream();
147: int c = 0;
148: byte[] buf = new byte[1024];
149: while ((c = inputStream.read(buf)) != -1) {
150: out.write(buf, 0, c);
151: }
152: out.flush();
153: return out.toByteArray();
154: } catch (IOException e) {
155: }
156: return in;
157: }
158:
159: //command line method
160: public static void main(String[] argv) throws IOException {
161: if (argv.length == 0) {
162: System.err.println(IECompatibleJpegInputStream.class
163: .getName()
164: + " removes headers from jpeg files");
165: System.err
166: .println("it requires 2 parameters , the input jpeg and the output jpeg");
167: System.exit(1);
168: } else if (argv.length == 2) {
169: File file = new File(argv[0]);
170: if (!file.exists()) {
171: System.err.println("can't convert non existing file"
172: + file.getPath());
173: }
174: File out = new File(argv[1]);
175: InputStream in = new IECompatibleJpegInputStream(
176: new FileInputStream(file));
177: OutputStream fos = new BufferedOutputStream(
178: new FileOutputStream(out));
179: int c = 0;
180: while ((c = in.read()) != -1) {
181: fos.write(c);
182: }
183: in.close();
184: fos.flush();
185: fos.close();
186: }
187: }
188: }
|