Annotation of java/classes/org/w3c/tools/jpeg/JpegCommentWriter.java, revision 1.6
1.1 ylafon 1: // JpegCOmmentWriter.java
1.6 ! ylafon 2: // $Id: JpegCommentWriter.java,v 1.5 2000/10/19 15:53:26 ylafon Exp $
1.1 ylafon 3: // (c) COPYRIGHT MIT, INRIA and Keio, 1999.
4: // Please first read the full copyright statement in file COPYRIGHT.html
5:
6: package org.w3c.tools.jpeg;
7:
1.4 ylafon 8: import java.io.ByteArrayOutputStream;
9: import java.io.File;
10: import java.io.FileInputStream;
11: import java.io.IOException;
12: import java.io.InputStream;
13: import java.io.OutputStream;
14: import java.io.OutputStreamWriter;
15: import java.io.UnsupportedEncodingException;
16: import java.io.Writer;
1.1 ylafon 17:
1.3 ylafon 18: /**
19: * Allow you to write text comments to jpeg stream
20: * Some code has been adapted from wrjpgcom.c from The Independent JPEG Group
21: */
1.1 ylafon 22: public class JpegCommentWriter extends Writer {
23:
24: // usual debug stuff
25: private static final boolean debug = true;
26: InputStream inJpegData;
27: OutputStream outJpegData;
1.6 ! ylafon 28: final ByteArrayOutputStream byteStream;
1.1 ylafon 29: OutputStreamWriter osw;
30: boolean init;
31: int lastMarker;
1.4 ylafon 32:
1.1 ylafon 33: /**
34: * Create a JpegCommentWriter, using an Input stream as the jpeg binary
35: * source, and writing in the output stream
1.6 ! ylafon 36: *
1.1 ylafon 37: * @param out, the output stream where the image will be written
1.6 ! ylafon 38: * @param in, the input stream of the jpeg file, it MUST point to the
! 39: * beginning of the jpeg to avoid problems
1.1 ylafon 40: */
41: public JpegCommentWriter(OutputStream out, InputStream in) {
1.6 ! ylafon 42: super(out);
! 43: inJpegData = in;
! 44: outJpegData = out;
! 45: byteStream = new ByteArrayOutputStream(65536);
! 46: osw = new OutputStreamWriter(byteStream);
! 47: init = false;
1.1 ylafon 48: }
1.4 ylafon 49:
1.1 ylafon 50: /**
51: * Create a JpegCommentWriter, using an Input stream as the jpeg binary
52: * source, and writing in the output stream
1.6 ! ylafon 53: *
1.1 ylafon 54: * @param out, the output stream where the image will be written
1.6 ! ylafon 55: * @param in, the input stream of the jpeg file, it MUST point to the
! 56: * beginning of the jpeg to avoid problems
1.1 ylafon 57: * @param enc, the encoding name used when you write comments
58: */
59: public JpegCommentWriter(OutputStream out, InputStream in, String enc)
1.6 ! ylafon 60: throws UnsupportedEncodingException {
! 61: super(out);
! 62: inJpegData = in;
! 63: outJpegData = out;
! 64: byteStream = new ByteArrayOutputStream(65536);
! 65: osw = new OutputStreamWriter(byteStream, enc);
! 66: init = false;
! 67: }
! 68:
! 69: /**
! 70: * The usual debugging tool
! 71: */
! 72: public static void main(String args[]) {
! 73: try {
! 74: File jpegFile = new File(args[0]);
! 75: JpegCommentWriter jcw = new JpegCommentWriter(System.out,
! 76: new FileInputStream(jpegFile));
! 77: jcw.write(args[1]);
! 78: jcw.close();
! 79: } catch (Exception ex) {
! 80: ex.printStackTrace();
! 81: }
! 82: }
1.4 ylafon 83:
1.1 ylafon 84: /**
85: * gets the encoding used by the comment writer
86: */
87: public String getEncoding() {
1.6 ! ylafon 88: return osw.getEncoding();
1.1 ylafon 89: }
1.4 ylafon 90:
1.1 ylafon 91: /**
92: * write one character
93: */
1.6 ! ylafon 94: public void write(int ch)
! 95: throws IOException {
! 96: osw.write(ch);
1.1 ylafon 97: }
98:
99: /**
100: * write an array of characters
101: */
102: public void write(char[] buffer)
1.6 ! ylafon 103: throws IOException {
! 104: osw.write(buffer);
1.1 ylafon 105: }
106:
107: /**
108: * write a portion of an array of characters
109: */
110: public void write(char[] buffer, int off, int len)
1.6 ! ylafon 111: throws IOException {
! 112: osw.write(buffer, off, len);
1.1 ylafon 113: }
1.4 ylafon 114:
1.1 ylafon 115: /**
116: * Write a String
117: */
118: public void write(String s)
1.6 ! ylafon 119: throws IOException {
! 120: osw.write(s);
1.1 ylafon 121: }
122:
1.6 ! ylafon 123: /**
1.1 ylafon 124: * Write a portion of a String
125: */
126: public void write(String s, int off, int len)
1.6 ! ylafon 127: throws IOException {
! 128: osw.write(s, off, len);
1.1 ylafon 129: }
130:
131: public void flush()
1.6 ! ylafon 132: throws IOException {
! 133: if (!init) {
! 134: dupFirstHeaders();
! 135: init = true;
! 136: }
! 137: osw.flush();
! 138: byte[] buffer;
! 139: synchronized (byteStream) {
! 140: buffer = byteStream.toByteArray();
! 141: byteStream.reset();
! 142: }
! 143: int buflen = buffer.length;
! 144: int curlen;
! 145: int curpos = 0;
! 146: // write the comments, using chunks if necessary
! 147: while (buflen > 0) {
! 148: writeMarker(Jpeg.M_COM);
! 149: curlen = Math.min(Jpeg.M_MAX_COM_LENGTH, buflen);
! 150: writeMarkerLength(curlen + 2);
! 151: outJpegData.write(buffer, curpos, curlen);
! 152: curpos += curlen;
! 153: buflen -= curlen;
! 154: }
1.1 ylafon 155: }
156:
157: public void close()
1.6 ! ylafon 158: throws IOException {
! 159: flush();
! 160: byte[] buf = new byte[1024];
! 161: int got;
! 162: // first dump the last marker
! 163: writeMarker(lastMarker);
! 164: // then the end of the file
! 165: do {
! 166: got = inJpegData.read(buf);
! 167: if (got < 0)
! 168: break;
! 169: outJpegData.write(buf, 0, got);
! 170: } while (got >= 0);
1.1 ylafon 171: }
172:
173: /**
174: * copy the marker and return it
175: */
176: protected int dupFirstMarker()
1.6 ! ylafon 177: throws IOException, JpegException {
! 178: int c1, c2;
! 179: c1 = inJpegData.read();
! 180: c2 = inJpegData.read();
! 181: if (c1 != 0xFF || c2 != Jpeg.M_SOI)
! 182: throw new JpegException("Not a JPEG file");
! 183: outJpegData.write(c1);
! 184: outJpegData.write(c2);
! 185: return c2;
1.1 ylafon 186: }
187:
188: /**
189: * read 2 bytes and create an integer out of it
190: */
1.6 ! ylafon 191: protected int read2bytes()
! 192: throws IOException, JpegException {
! 193: int c1, c2;
! 194: c1 = inJpegData.read();
! 195: if (c1 == -1)
! 196: throw new JpegException("Premature EOF in JPEG file");
! 197: c2 = inJpegData.read();
! 198: if (c2 == -1)
! 199: throw new JpegException("Premature EOF in JPEG file");
! 200: return (((int) c1) << 8) + ((int) c2);
1.1 ylafon 201: }
202:
203: /**
204: * get the next marker, and eat extra bytes
205: */
1.6 ! ylafon 206: protected int nextMarker()
! 207: throws IOException {
! 208: int discarded_bytes = 0;
! 209: int c;
1.1 ylafon 210:
211: /* Find 0xFF byte; count and skip any non-FFs. */
1.6 ! ylafon 212: c = inJpegData.read();
! 213: while (c != 0xFF)
! 214: c = inJpegData.read();
1.1 ylafon 215:
216: /* Get marker code byte, swallowing any duplicate FF bytes. Extra FFs
1.6 ! ylafon 217: * are legal as pad bytes, so don't count them in discarded_bytes.
1.1 ylafon 218: */
1.6 ! ylafon 219: do {
! 220: c = inJpegData.read();
! 221: } while (c == 0xFF);
1.1 ylafon 222:
1.6 ! ylafon 223: return c;
1.1 ylafon 224: }
225:
226: /**
227: * skip the body after a marker
228: */
1.6 ! ylafon 229: protected void skipVariable()
! 230: throws IOException, JpegException {
! 231: long len = (long) read2bytes() - 2;
! 232: if (len < 0)
! 233: throw new JpegException("Erroneous JPEG marker length");
! 234: while (len > 0) {
! 235: long saved = inJpegData.skip(len);
! 236: if (saved < 0)
! 237: throw new IOException("Error while reading jpeg stream");
! 238: len -= saved;
! 239: }
1.1 ylafon 240: }
241:
242: /**
243: * dup the marker and the body
244: */
1.6 ! ylafon 245: protected void dupHeader(int marker)
! 246: throws IOException, JpegException {
! 247: int len = read2bytes();
! 248: if (len < 2)
! 249: throw new JpegException("Erroneous JPEG marker length");
! 250: // dump the marker
! 251: writeMarker(marker);
! 252: // write the length
! 253: writeMarkerLength(len);
! 254: // and now copy the bytes
! 255: byte[] buf = new byte[1024];
! 256: int got;
! 257: len -= 2;
! 258: while (len > 0) {
! 259: got = inJpegData.read(buf, 0, Math.min(buf.length, len));
! 260: if (got < 0)
! 261: throw new IOException("Error while reading jpeg stream (EOF)");
! 262: outJpegData.write(buf, 0, got);
! 263: len -= got;
! 264: }
! 265: }
! 266:
! 267: protected void writeMarker(int marker)
! 268: throws IOException {
! 269: outJpegData.write(0xFF);
! 270: outJpegData.write(marker);
1.1 ylafon 271: }
272:
273: protected void writeMarkerLength(int len)
1.6 ! ylafon 274: throws IOException {
! 275: outJpegData.write((len >> 8) & 0xFF);
! 276: outJpegData.write(len & 0xFF);
! 277: }
1.1 ylafon 278:
279: /**
280: * the the first headers until a SOF parker is found
281: */
1.6 ! ylafon 282: protected void dupFirstHeaders()
! 283: throws IOException {
! 284: int marker;
! 285: // first duplicate the header and make sure it is valid
! 286: try {
! 287: dupFirstMarker();
! 288: } catch (JpegException ex) {
! 289: if (debug) {
! 290: ex.printStackTrace();
! 291: }
! 292: throw new IOException(ex.getMessage());
! 293: }
! 294: // now dump the headers
! 295:
! 296: while (true) {
! 297: marker = nextMarker();
! 298: switch (marker) {
! 299: case Jpeg.M_SOF0: /* Baseline */
! 300: case Jpeg.M_SOF1: /* Extended sequential, Huffman */
! 301: case Jpeg.M_SOF2: /* Progressive, Huffman */
! 302: case Jpeg.M_SOF3: /* Lossless, Huffman */
! 303: case Jpeg.M_SOF5: /* Differential sequential, Huffman */
! 304: case Jpeg.M_SOF6: /* Differential progressive, Huffman */
! 305: case Jpeg.M_SOF7: /* Differential lossless, Huffman */
! 306: case Jpeg.M_SOF9: /* Extended sequential, arithmetic */
! 307: case Jpeg.M_SOF10: /* Progressive, arithmetic */
! 308: case Jpeg.M_SOF11: /* Lossless, arithmetic */
! 309: case Jpeg.M_SOF13: /* Differential sequential, arithmetic */
! 310: case Jpeg.M_SOF14: /* Differential progressive, arithmetic */
! 311: case Jpeg.M_SOF15: /* Differential lossless, arithmetic */
! 312: // bye!
! 313: lastMarker = marker;
! 314: return;
! 315: case Jpeg.M_SOS:
! 316: break;
! 317: case Jpeg.M_EOI:
! 318: lastMarker = marker;
! 319: return;
! 320: case Jpeg.M_APP12:
! 321: case Jpeg.M_COM: // remove previous comments
! 322: try {
! 323: skipVariable();
! 324: } catch (JpegException jex) {
! 325: if (debug)
! 326: jex.printStackTrace();
! 327: throw new IOException(jex.getMessage());
! 328: }
! 329: break;
! 330: default:
! 331: try {
! 332: dupHeader(marker);
! 333: } catch (JpegException jex) {
! 334: if (debug)
! 335: jex.printStackTrace();
! 336: throw new IOException(jex.getMessage());
! 337: }
! 338: break;
! 339: }
! 340: }
1.1 ylafon 341: }
342: }
Webmaster