1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.pdfbox.io; 18 19 import java.io.FilterOutputStream; 20 import java.io.IOException; 21 import java.io.OutputStream; 22 23 /** 24 * This class represents an ASCII85 output stream. 25 * 26 * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a> 27 * @version $Revision: 1.7 $ 28 */ 29 public class ASCII85OutputStream extends FilterOutputStream 30 { 31 32 private int lineBreak; 33 private int count; 34 35 private byte[] indata; 36 private byte[] outdata; 37 38 /** 39 * Function produces five ASCII printing characters from 40 * four bytes of binary data. 41 */ 42 private int maxline; 43 private boolean flushed; 44 private char terminator; 45 46 /** 47 * Constructor. 48 * 49 * @param out The output stream to write to. 50 */ 51 public ASCII85OutputStream( OutputStream out ) 52 { 53 super( out ); 54 lineBreak = 36*2; 55 maxline = 36*2; 56 count=0; 57 indata=new byte[4]; 58 outdata=new byte[5]; 59 flushed=true; 60 terminator='~'; 61 } 62 63 /** 64 * This will set the terminating character. 65 * 66 * @param term The terminating character. 67 */ 68 public void setTerminator(char term) 69 { 70 if(term<118 || term>126 || term=='z') 71 { 72 throw new IllegalArgumentException("Terminator must be 118-126 excluding z"); 73 } 74 terminator=term; 75 } 76 77 /** 78 * This will get the terminating character. 79 * 80 * @return The terminating character. 81 */ 82 public char getTerminator() 83 { 84 return terminator; 85 } 86 87 /** 88 * This will set the line length that will be used. 89 * 90 * @param l The length of the line to use. 91 */ 92 public void setLineLength(int l) 93 { 94 if( lineBreak > l ) 95 { 96 lineBreak = l; 97 } 98 maxline=l; 99 } 100 101 /** 102 * This will get the length of the line. 103 * 104 * @return The line length attribute. 105 */ 106 public int getLineLength() 107 { 108 return maxline; 109 } 110 111 /** 112 * This will transform the next four ascii bytes. 113 */ 114 private final void transformASCII85() 115 { 116 long word; 117 word=( (((indata[0] << 8) | (indata[1] &0xFF)) << 16) | 118 ( (indata[2] & 0xFF) << 8) | (indata[3] & 0xFF) 119 ) & 0xFFFFFFFFL; 120 // System.out.println("word=0x"+Long.toString(word,16)+" "+word); 121 122 if (word == 0 ) 123 { 124 outdata[0]=(byte)'z'; 125 outdata[1]=0; 126 return; 127 } 128 long x; 129 x=word/(85L*85L*85L*85L); 130 // System.out.println("x0="+x); 131 outdata[0]=(byte)(x+'!'); 132 word-=x*85L*85L*85L*85L; 133 134 x=word/(85L*85L*85L); 135 // System.out.println("x1="+x); 136 outdata[1]=(byte)(x+'!'); 137 word-=x*85L*85L*85L; 138 139 x=word/(85L*85L); 140 // System.out.println("x2="+x); 141 outdata[2]=(byte)(x+'!'); 142 word-=x*85L*85L; 143 144 x=word/85L; 145 // System.out.println("x3="+x); 146 outdata[3]=(byte)(x+'!'); 147 148 // word-=x*85L; 149 150 // System.out.println("x4="+(word % 85L)); 151 outdata[4]=(byte)((word%85L)+'!'); 152 } 153 154 /** 155 * This will write a single byte. 156 * 157 * @param b The byte to write. 158 * 159 * @throws IOException If there is an error writing to the stream. 160 */ 161 public final void write(int b) throws IOException 162 { 163 flushed=false; 164 indata[count++]=(byte)b; 165 if(count < 4 ) 166 { 167 return; 168 } 169 transformASCII85(); 170 for(int i=0;i<5;i++) 171 { 172 if(outdata[i]==0) 173 { 174 break; 175 } 176 out.write(outdata[i]); 177 if(--lineBreak==0) 178 { 179 out.write('\n'); 180 lineBreak=maxline; 181 } 182 } 183 count = 0; 184 } 185 186 /** 187 * This will write a chunk of data to the stream. 188 * 189 * @param b The byte buffer to read from. 190 * @param off The offset into the buffer. 191 * @param sz The number of bytes to read from the buffer. 192 * 193 * @throws IOException If there is an error writing to the underlying stream. 194 */ 195 public final void write(byte[] b,int off, int sz) throws IOException 196 { 197 for(int i=0;i<sz;i++) 198 { 199 if(count < 3) 200 { 201 indata[count++]=b[off+i]; 202 } 203 else 204 { 205 write(b[off+i]); 206 } 207 } 208 } 209 210 /** 211 * This will flush the data to the stream. 212 * 213 * @throws IOException If there is an error writing the data to the stream. 214 */ 215 public final void flush() throws IOException 216 { 217 if(flushed) 218 { 219 return; 220 } 221 if(count > 0 ) 222 { 223 for( int i=count; i<4; i++ ) 224 { 225 indata[i]=0; 226 } 227 transformASCII85(); 228 if(outdata[0]=='z') 229 { 230 for(int i=0;i<5;i++) // expand 'z', 231 { 232 outdata[i]=(byte)'!'; 233 } 234 } 235 for(int i=0;i<count+1;i++) 236 { 237 out.write(outdata[i]); 238 if(--lineBreak==0) 239 { 240 out.write('\n'); 241 lineBreak=maxline; 242 } 243 } 244 } 245 if(--lineBreak==0) 246 { 247 out.write('\n'); 248 } 249 out.write(terminator); 250 out.write('\n'); 251 count = 0; 252 lineBreak=maxline; 253 flushed=true; 254 super.flush(); 255 } 256 257 /** 258 * This will close the stream. 259 * 260 * @throws IOException If there is an error closing the wrapped stream. 261 */ 262 public void close() throws IOException 263 { 264 try 265 { 266 super.close(); 267 } 268 finally 269 { 270 indata=outdata=null; 271 } 272 } 273 274 /** 275 * This will flush the stream. 276 * 277 * @throws Throwable If there is an error. 278 */ 279 protected void finalize() throws Throwable 280 { 281 try 282 { 283 flush(); 284 } 285 finally 286 { 287 super.finalize(); 288 } 289 } 290 }