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