001: /**
002: * Copyright (c) 2003-2005, www.pdfbox.org
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * 1. Redistributions of source code must retain the above copyright notice,
009: * this list of conditions and the following disclaimer.
010: * 2. Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: * 3. Neither the name of pdfbox; nor the names of its
014: * contributors may be used to endorse or promote products derived from this
015: * software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
021: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: *
028: * http://www.pdfbox.org
029: *
030: */package org.pdfbox.filter;
031:
032: import java.io.ByteArrayOutputStream;
033: import java.io.IOException;
034: import java.io.InputStream;
035: import java.io.OutputStream;
036: import java.text.SimpleDateFormat;
037: import java.util.Date;
038:
039: import org.pdfbox.cos.COSDictionary;
040: import org.pdfbox.cos.COSName;
041:
042: /**
043: * This is a filter for the CCITTFax Decoder.
044: *
045: * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
046: * @author Marcel Kammer
047: * @author Paul King
048: * @version $Revision: 1.12 $
049: */
050: public class CCITTFaxDecodeFilter implements Filter {
051: // Filter will write 15 TAG's
052: // If you add or remove TAG's you will have to modify this value
053: private static final int TAG_COUNT = 15;
054:
055: // HEADERLENGTH(fix 8 Bytes) plus ImageLength(variable)
056: private int offset = 8;
057:
058: // Bytecounter for Bytes that will be written after the TAG-DICTIONARY
059: private int tailingBytesCount = 0;
060:
061: // Bytes to write after TAG-DICTIONARY
062: private final ByteArrayOutputStream tailer = new ByteArrayOutputStream();
063:
064: /**
065: * Constructor.
066: */
067: public CCITTFaxDecodeFilter() {
068: }
069:
070: /**
071: * This will decode some compressed data.
072: *
073: * @param compressedData
074: * The compressed byte stream.
075: * @param result
076: * The place to write the uncompressed byte stream.
077: * @param options
078: * The options to use to encode the data.
079: *
080: * @throws IOException
081: * If there is an error decompressing the stream.
082: */
083: public void decode(InputStream compressedData, OutputStream result,
084: COSDictionary options) throws IOException {
085: // log.warn( "Warning: CCITTFaxDecode.decode is not implemented yet,
086: // skipping this stream." );
087:
088: // Get ImageParams from PDF
089: COSDictionary dict = (COSDictionary) options
090: .getDictionaryObject("DecodeParms");
091: int width = options.getInt("Width");
092: int height = options.getInt("Height");
093: int length = options.getInt(COSName.LENGTH);
094: int compressionType = dict.getInt("K");
095: boolean blackIs1 = dict.getBoolean("BlackIs1", false);
096:
097: // HEADER-INFO and starting point of TAG-DICTIONARY
098: writeTagHeader(result, length);
099:
100: // IMAGE-DATA
101: int i = 0;
102: //int sum = 0;
103: byte[] buffer = new byte[32768];
104: int lentoread = length;
105:
106: while ((lentoread > 0)
107: && ((i = compressedData.read(buffer, 0, Math.min(
108: lentoread, 32768))) != -1)) {
109: //sum += i;
110: result.write(buffer, 0, i);
111: lentoread = lentoread - i;
112: }
113:
114: // If lentoread is > 0 then we need to write out some padding to equal the header
115: // We'll use what we have in the buffer it's just padding after all
116: while (lentoread > 0) {
117: result.write(buffer, 0, Math.min(lentoread, 32768));
118: lentoread = lentoread - Math.min(lentoread, 32738);
119: }
120: //System.out.println("Gelesen: " + sum);
121:
122: // TAG-COUNT
123: writeTagCount(result);
124:
125: // WIDTH 0x0100
126: writeTagWidth(result, width);
127:
128: // HEIGHT 0x0101
129: writeTagHeight(result, height);
130:
131: // BITSPERSAMPLE 0x0102
132: // Always 1 for CCITTFax
133: writeTagBitsPerSample(result, 1);
134:
135: // COMPRESSION 0x0103
136: writeTagCompression(result, compressionType);
137:
138: // PHOTOMETRIC 0x0106
139: writeTagPhotometric(result, blackIs1);
140:
141: // STRIPOFFSET 0x0111
142: // HERE ALWAYS 8, because ImageData comes before TAG-DICTIONARY
143: writeTagStripOffset(result, 8);
144:
145: // ORIENTATION 0x0112
146: writeTagOrientation(result, 1);
147:
148: // SamplesPerPixel 0x0115
149: writeTagSamplesPerPixel(result, 1);
150:
151: // RowsPerStrip 0x0116
152: writeTagRowsPerStrip(result, height);
153:
154: // Stripcount 0x0117
155: writeTagStripByteCount(result, length);
156:
157: // XRESOLUTION 0x011A
158: // HERE: 200 DPI
159: writeTagXRes(result, 200, 1);
160:
161: // YRESOLITION 0x011B
162: // HERE: 200 DPI
163: writeTagYRes(result, 200, 1);
164:
165: // ResolutionUnit 0x0128
166: // HERE: DPI
167: writeTagResolutionUnit(result, 2);
168:
169: // SOFTWARE 0x0131
170: // minimum 4 chars
171: writeTagSoftware(result, "pdfbox".getBytes());
172:
173: // DATE AND TIME 0x0132
174: writeTagDateTime(result, new Date());
175:
176: // END OF TAG-DICT
177: writeTagTailer(result);
178: }
179:
180: private void writeTagHeader(OutputStream result, int length)
181: throws IOException {
182: byte[] header = { 'M', 'M', 0, '*' };// Big-endian
183: result.write(header);
184:
185: // Add imagelength to offset
186: offset += length;
187:
188: // OFFSET TAG-DICTIONARY
189: int i1 = offset / 16777216;//=value/(256*256*256)
190: int i2 = (offset - i1 * 16777216) / 65536;
191: int i3 = (offset - i1 * 16777216 - i2 * 65536) / 256;
192: int i4 = offset % 256;
193: result.write(i1);
194: result.write(i2);
195: result.write(i3);
196: result.write(i4);
197: }
198:
199: private void writeTagCount(OutputStream result) throws IOException {
200: result.write(TAG_COUNT / 256);
201: result.write(TAG_COUNT % 256);// tagCount
202: }
203:
204: private void writeTagWidth(OutputStream result, int width)
205: throws IOException {
206: // @todo width berechnen
207:
208: // TAG-ID 100
209: result.write(1);
210: result.write(0);
211:
212: // TAG-TYPE SHORT=3
213: result.write(0);
214: result.write(3);
215:
216: // TAG-LENGTH = 1
217: result.write(0);
218: result.write(0);
219: result.write(0);
220: result.write(1);
221:
222: // TAG-VALUE = width
223: result.write(width / 256);
224: result.write(width % 256);
225: result.write(0);// SHORT=0
226: result.write(0);// SHORT=0
227:
228: }
229:
230: private void writeTagHeight(OutputStream result, int height)
231: throws IOException {
232: //@todo height berechnen
233: // TAG-ID 101
234: result.write(1);
235: result.write(1);
236:
237: // TAG-TYPE SHORT=3
238: result.write(0);
239: result.write(3);
240:
241: // TAG-LENGTH = 1
242: result.write(0);
243: result.write(0);
244: result.write(0);
245: result.write(1);
246:
247: // TAG-VALUE
248: result.write(height / 256);
249: result.write(height % 256);
250: result.write(0);// SHORT=0
251: result.write(0);// SHORT=0
252:
253: }
254:
255: private void writeTagBitsPerSample(OutputStream result, int value)
256: throws IOException {
257: // TAG-ID 102
258: result.write(1);
259: result.write(2);
260:
261: // TAG-TYPE SHORT=3
262: result.write(0);
263: result.write(3);
264:
265: // TAG-LENGTH = 1
266: result.write(0);
267: result.write(0);
268: result.write(0);
269: result.write(1);
270:
271: // TAG-VALUE
272: result.write(value / 256);
273: result.write(value % 256);
274: result.write(0);//SHORT=0
275: result.write(0);//SHORT=0
276:
277: }
278:
279: /**
280: * Write the tag compression.
281: *
282: * @param result The stream to write to.
283: * @param type The type to write.
284: * @throws IOException If there is an error writing to the stream.
285: */
286: public void writeTagCompression(OutputStream result, int type)
287: throws IOException {
288: // TAG-ID 103
289: result.write(1);
290: result.write(3);
291:
292: // TAG-TYPE SHORT=3
293: result.write(0);
294: result.write(3);
295:
296: // TAG-LEGNTH = 1
297: result.write(0);
298: result.write(0);
299: result.write(0);
300: result.write(1);
301:
302: // TAG-VALUE
303: //@todo typ eintragen; hier immer 4
304: result.write(0);
305: if (type < 0) {
306: result.write(4);// G4
307: } else if (type == 0) {
308: result.write(3);// G3-1D
309: } else {
310: result.write(2);// G3-2D
311: }
312: result.write(0);
313: result.write(0);
314:
315: }
316:
317: private void writeTagPhotometric(OutputStream result,
318: boolean blackIs1) throws IOException {
319: // TAG-ID 106
320: result.write(1);
321: result.write(6);
322:
323: // TAG-TYPE SHORT
324: result.write(0);
325: result.write(3);
326:
327: // TAG-LENGTH = 1
328: result.write(0);
329: result.write(0);
330: result.write(0);
331: result.write(1);
332:
333: // TAG-VALUE
334: result.write(0);
335: if (blackIs1) {
336: result.write(1);
337: } else {
338: result.write(0);
339: }
340: result.write(0);// SHORT=0
341: result.write(0);// SHORT=0
342:
343: }
344:
345: private void writeTagStripOffset(OutputStream result, int value)
346: throws IOException {
347: // TAG-ID 111
348: result.write(1);
349: result.write(17);
350:
351: // TAG-TYPE LONG=4
352: result.write(0);
353: result.write(4);
354:
355: // TAG-LENGTH=1
356: result.write(0);
357: result.write(0);
358: result.write(0);
359: result.write(1);
360:
361: // TAG-VALUE = 8 //VOR TAG-DICTIONARY
362: int i1 = value / 16777216;//=value/(256*256*256)
363: int i2 = (value - i1 * 16777216) / 65536;
364: int i3 = (value - i1 * 16777216 - i2 * 65536) / 256;
365: int i4 = value % 256;
366: result.write(i1);
367: result.write(i2);
368: result.write(i3);
369: result.write(i4);
370:
371: }
372:
373: private void writeTagSamplesPerPixel(OutputStream result, int value)
374: throws IOException {
375: // TAG-ID 115
376: result.write(1);
377: result.write(21);
378:
379: // TAG-TYPE SHORT=3
380: result.write(0);
381: result.write(3);
382:
383: // TAG-LENGTH=1
384: result.write(0);
385: result.write(0);
386: result.write(0);
387: result.write(1);
388:
389: // TAG-VALUE
390: result.write(value / 256);
391: result.write(value % 256);
392: result.write(0);// SHORT=0
393: result.write(0);// SHORT=0
394:
395: }
396:
397: private void writeTagRowsPerStrip(OutputStream result, int value)
398: throws IOException {
399: // TAG-ID 116
400: result.write(1);
401: result.write(22);
402:
403: // TAG-TYPE SHORT=3
404: result.write(0);
405: result.write(3);
406:
407: // TAG-LENGTH=1
408: result.write(0);
409: result.write(0);
410: result.write(0);
411: result.write(1);
412:
413: // TAG-VALUE
414: result.write(value / 256);
415: result.write(value % 256);
416: result.write(0);// SHORT=0
417: result.write(0);// SHORT=0
418:
419: }
420:
421: private void writeTagStripByteCount(OutputStream result, int value)
422: throws IOException {
423: //@todo value auswerten
424: // TAG-ID 117
425: result.write(1);
426: result.write(23);
427:
428: // TAG-TYPE LONG=4
429: result.write(0);
430: result.write(4);
431:
432: // TAG-LENGTH = 1
433: result.write(0);
434: result.write(0);
435: result.write(0);
436: result.write(1);
437:
438: // TAG-VALUE
439: int i1 = value / 16777216;//=value/(256*256*256)
440: int i2 = (value - i1 * 16777216) / 65536;
441: int i3 = (value - i1 * 16777216 - i2 * 65536) / 256;
442: int i4 = value % 256;
443: result.write(i1);
444: result.write(i2);
445: result.write(i3);
446: result.write(i4);
447:
448: }
449:
450: private void writeTagXRes(OutputStream result, int value1,
451: int value2) throws IOException {
452: // TAG-ID 11A
453: result.write(1);
454: result.write(26);
455:
456: // TAG-TYPE RATIONAL=5
457: result.write(0);
458: result.write(5);
459:
460: // TAG-LENGTH=1
461: result.write(0);
462: result.write(0);
463: result.write(0);
464: result.write(1);
465:
466: // TAG-VALUE=OFFSET TO RATIONAL
467: int valueOffset = offset + 6 + 12 * TAG_COUNT + tailer.size();
468: int i1 = valueOffset / 16777216;//=value/(256*256*256)
469: int i2 = (valueOffset - i1 * 16777216) / 65536;
470: int i3 = (valueOffset - i1 * 16777216 - i2 * 65536) / 256;
471: int i4 = valueOffset % 256;
472: result.write(i1);
473: result.write(i2);
474: result.write(i3);
475: result.write(i4);
476:
477: i1 = value1 / 16777216;
478: i2 = (value1 - i1 * 16777216) / 65536;
479: i3 = (value1 - i1 * 16777216 - i2 * 65536) / 256;
480: i4 = value1 % 256;
481: tailer.write(i1);
482: tailer.write(i2);
483: tailer.write(i3);
484: tailer.write(i4);
485:
486: i1 = value2 / 16777216;
487: i2 = (value2 - i1 * 16777216) / 65536;
488: i3 = (value2 - i1 * 16777216 - i2 * 65536) / 256;
489: i4 = value2 % 256;
490: tailer.write(i1);
491: tailer.write(i2);
492: tailer.write(i3);
493: tailer.write(i4);
494:
495: tailingBytesCount += 8;
496: }
497:
498: private void writeTagYRes(OutputStream result, int value1,
499: int value2) throws IOException {
500: // TAG-ID 11B
501: result.write(1);
502: result.write(27);
503:
504: // TAG-TYPE RATIONAL=5
505: result.write(0);
506: result.write(5);
507:
508: // TAG-LENGTH=1
509: result.write(0);
510: result.write(0);
511: result.write(0);
512: result.write(1);
513:
514: // TAG-VALUE=OFFSET TO RATIONAL
515: int valueOffset = offset + 6 + 12 * TAG_COUNT + tailer.size();
516: int i1 = valueOffset / 16777216;//=value/(256*256*256)
517: int i2 = (valueOffset - i1 * 16777216) / 65536;
518: int i3 = (valueOffset - i1 * 16777216 - i2 * 65536) / 256;
519: int i4 = valueOffset % 256;
520: result.write(i1);
521: result.write(i2);
522: result.write(i3);
523: result.write(i4);
524:
525: i1 = value1 / 16777216;
526: i2 = (value1 - i1 * 16777216) / 65536;
527: i3 = (value1 - i1 * 16777216 - i2 * 65536) / 256;
528: i4 = value1 % 256;
529: tailer.write(i1);
530: tailer.write(i2);
531: tailer.write(i3);
532: tailer.write(i4);
533:
534: i1 = value2 / 16777216;
535: i2 = (value2 - i1 * 16777216) / 65536;
536: i3 = (value2 - i1 * 16777216 - i2 * 65536) / 256;
537: i4 = value2 % 256;
538: tailer.write(i1);
539: tailer.write(i2);
540: tailer.write(i3);
541: tailer.write(i4);
542:
543: tailingBytesCount += 8;
544: }
545:
546: private void writeTagResolutionUnit(OutputStream result, int value)
547: throws IOException {
548: // TAG-ID 128
549: result.write(1);
550: result.write(40);
551:
552: // TAG-TYPE SHORT=3
553: result.write(0);
554: result.write(3);
555:
556: // TAG-LENGTH = 1
557: result.write(0);
558: result.write(0);
559: result.write(0);
560: result.write(1);
561:
562: // TAG-VALUE
563: result.write(value / 256);
564: result.write(value % 256);
565: result.write(0);// SHORT=0
566: result.write(0);// SHORT=0
567:
568: }
569:
570: private void writeTagOrientation(OutputStream result, int value)
571: throws IOException {
572: // TAG-ID 112
573: result.write(1);
574: result.write(18);
575:
576: // TAG-TYPE SHORT = 3
577: result.write(0);
578: result.write(3);
579:
580: // TAG-LENGTH=1
581: result.write(0);
582: result.write(0);
583: result.write(0);
584: result.write(1);
585:
586: // TAG-VALUE
587: result.write(value / 256);
588: result.write(value % 256);
589: result.write(0);// SHORT=0
590: result.write(0);// SHORT=0
591:
592: }
593:
594: private void writeTagTailer(OutputStream result) throws IOException {
595: // END OF TAG-DICTIONARY
596: result.write(0);
597: result.write(0);
598: result.write(0);
599: result.write(0);
600:
601: // TAILER WITH VALUES OF RATIONALFIELD's
602: result.write(tailer.toByteArray());
603: }
604:
605: private void writeTagSoftware(OutputStream result, byte[] text)
606: throws IOException {
607: // TAG-ID 131
608: result.write(1);
609: result.write(49);
610:
611: // TAG-TYPE ASCII=2
612: result.write(0);
613: result.write(2);
614:
615: // TAG-LENGTH=id.length+1
616: result.write(0);
617: result.write(0);
618: result.write((text.length + 1) / 256);
619: result.write((text.length + 1) % 256);
620:
621: // TAG-VALUE
622: int valueOffset = offset + 6 + 12 * TAG_COUNT + tailer.size();
623: int i1 = valueOffset / 16777216;//=value/(256*256*256)
624: int i2 = (valueOffset - i1 * 16777216) / 65536;
625: int i3 = (valueOffset - i1 * 16777216 - i2 * 65536) / 256;
626: int i4 = valueOffset % 256;
627: result.write(i1);
628: result.write(i2);
629: result.write(i3);
630: result.write(i4);
631:
632: tailer.write(text);
633: tailer.write(0);
634: tailingBytesCount += text.length + 1;
635: }
636:
637: private void writeTagDateTime(OutputStream result, Date date)
638: throws IOException {
639: // TAG-ID 132
640: result.write(1);
641: result.write(50);
642:
643: // TAG-TYPE ASCII=2
644: result.write(0);
645: result.write(2);
646:
647: // TAG-LENGTH=20
648: result.write(0);
649: result.write(0);
650: result.write(0);
651: result.write(20);
652:
653: // TAG-VALUE
654: int valueOffset = offset + 6 + 12 * TAG_COUNT + tailer.size();
655: int i1 = valueOffset / 16777216;//=value/(256*256*256)
656: int i2 = (valueOffset - i1 * 16777216) / 65536;
657: int i3 = (valueOffset - i1 * 16777216 - i2 * 65536) / 256;
658: int i4 = valueOffset % 256;
659: result.write(i1);
660: result.write(i2);
661: result.write(i3);
662: result.write(i4);
663:
664: SimpleDateFormat sdf = new SimpleDateFormat(
665: "yyyy:MM:dd HH:mm:ss");
666: String datetime = sdf.format(date);
667: tailer.write(datetime.getBytes());
668: tailer.write(0);
669:
670: tailingBytesCount += 20;
671: }
672:
673: /**
674: * This will encode some data.
675: *
676: * @param rawData
677: * The raw data to encode.
678: * @param result
679: * The place to write to encoded results to.
680: * @param options
681: * The options to use to encode the data.
682: *
683: * @throws IOException
684: * If there is an error compressing the stream.
685: */
686: public void encode(InputStream rawData, OutputStream result,
687: COSDictionary options) throws IOException {
688: System.err
689: .println("Warning: CCITTFaxDecode.encode is not implemented yet, skipping this stream.");
690: }
691: }
|