001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - 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: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.pdf.cos;
031:
032: import java.io.File;
033: import java.io.IOException;
034: import java.util.Iterator;
035: import java.util.Map;
036:
037: import de.intarsys.pdf.filter.FilterFactory;
038: import de.intarsys.pdf.filter.IFilter;
039: import de.intarsys.tools.collection.SingleObjectIterator;
040: import de.intarsys.tools.file.FileTools;
041:
042: /**
043: * An object representing stream data in a PDF document. Unlike a string, stream
044: * data is not restricted in length. Stream data may be encoded with the filter
045: * implementation.
046: */
047: public class COSStream extends COSCompositeObject {
048: public static final Object SLOT_BYTES = new Object();
049:
050: public static final COSName DK_DecodeParms = COSName
051: .constant("DecodeParms"); //$NON-NLS-1$
052:
053: public static final COSName DK_DP = COSName.constant("DP"); //$NON-NLS-1$
054:
055: public static final COSName DK_F = COSName.constant("F"); //$NON-NLS-1$
056:
057: public static final COSName DK_FDecodeParams = COSName
058: .constant("FDecodeParams"); //$NON-NLS-1$
059:
060: public static final COSName DK_FFilter = COSName
061: .constant("FFilter"); //$NON-NLS-1$
062:
063: public static final COSName DK_Filter = COSName.constant("Filter"); //$NON-NLS-1$
064:
065: public static final COSName DK_Length = COSName.constant("Length"); //$NON-NLS-1$
066:
067: public static final COSName DK_Resources = COSName
068: .constant("Resources"); //$NON-NLS-1$
069:
070: /**
071: * Create a new {@link COSStream}.
072: *
073: * @param dict
074: * An optional dictionary to be used as the streams dictionary.
075: * @return Create a new {@link COSStream}.
076: */
077: public static COSStream create(COSDictionary dict) {
078: COSStream result = new COSStream(dict);
079: result.beIndirect();
080: return result;
081: }
082:
083: /**
084: * The options or an array of options for filtering.
085: *
086: * @return The options or an array of options for filtering.
087: */
088: static public COSObject getDecodeParams(COSDictionary dict) {
089: if (isExternal(dict)) {
090: return dict.get(DK_FDecodeParams);
091: }
092: COSObject result = dict.get(DK_DP);
093: if (!result.isNull()) {
094: return result;
095: }
096: return dict.get(DK_DecodeParms);
097: }
098:
099: /**
100: * The options corresponding to the first occurence of the filter
101: * <code>name</code>.
102: *
103: * @return The options corresponding to the first occurence of the filter
104: * <code>name</code>.
105: */
106: static public COSDictionary getDecodeParams(COSDictionary dict,
107: COSName name) {
108: COSObject basicFilters = getFilters(dict);
109: if (basicFilters instanceof COSName) {
110: COSName filter = basicFilters.asName();
111: if (filter.equals(name)) {
112: return dict.get(COSStream.DK_DecodeParms)
113: .asDictionary();
114: }
115: } else if (basicFilters instanceof COSArray) {
116: COSArray filters = basicFilters.asArray();
117: int i = 0;
118: Iterator it = filters.iterator();
119: while (it.hasNext()) {
120: COSName filter = ((COSObject) it.next()).asName();
121: if (filter != null && filter.equals(name)) {
122: COSArray decodeParamsArray = dict.get(
123: COSStream.DK_DecodeParms).asArray();
124: if (decodeParamsArray != null) {
125: return decodeParamsArray.get(i).asDictionary();
126: }
127: }
128: i++;
129: }
130: }
131: return null;
132: }
133:
134: /**
135: * Return the filter or the collection of filters for the stream.
136: *
137: * @return The filter or the collection of filters for the stream.
138: */
139: static public COSObject getFilters(COSDictionary dict) {
140: if (isExternal(dict)) {
141: return dict.get(DK_FFilter);
142: }
143: COSObject result = dict.get(DK_F);
144: if (!result.isNull()) {
145: return result;
146: }
147: return dict.get(DK_Filter);
148: }
149:
150: /**
151: * <code>true</code> if the stream hs declared a filter <code>name</code>.
152: *
153: * @param name
154: * The name of the filter.
155: * @return <code>true</code> if the stream hs declared a filter
156: * <code>name</code>.
157: */
158: static public boolean hasFilter(COSDictionary dict, COSName name) {
159: COSObject filters = getFilters(dict);
160: if (filters.isNull()) {
161: return false;
162: }
163: if (filters instanceof COSName) {
164: return filters.equals(name);
165: }
166: if (filters instanceof COSArray) {
167: for (Iterator i = ((COSArray) filters).iterator(); i
168: .hasNext();) {
169: COSName filterName = ((COSObject) i.next()).asName();
170: if (filterName != null && filterName.equals(name)) {
171: return true;
172: }
173: }
174: }
175: return false;
176: }
177:
178: /**
179: * <code>true</code> if the stream dictionary contains the F key.
180: *
181: * @return <code>true</code> if the stream dictionary contains the F key.
182: */
183: static public boolean isExternal(COSDictionary dict) {
184: // check for F key
185: // if it is a Array or Name, it is used as an abbreviation for Filter
186: COSObject result = dict.get(DK_F);
187: if (!result.isNull()) {
188: if (result instanceof COSName || result instanceof COSArray) {
189: return false;
190: }
191: return true;
192: }
193: return false;
194: }
195:
196: /** The logical byte stream */
197: private byte[] decodedBytes;
198:
199: /** the dictionary describing the stream */
200: private COSDictionary dict;
201:
202: /** The physical byte stream */
203: private byte[] encodedBytes;
204:
205: /**
206: * COSStream constructor.
207: *
208: * @param newDict
209: * The stream dictionary for the new stream. Can be null, a new
210: * dictionary will be created.
211: */
212: protected COSStream(COSDictionary newDict) {
213: super ();
214: if (newDict == null) {
215: newDict = COSDictionary.create();
216: }
217: setDict(newDict);
218: }
219:
220: protected COSStream(COSStream stream) {
221: super (stream);
222: encodedBytes = stream.encodedBytes;
223: decodedBytes = stream.decodedBytes;
224: // if (stream.filteredBytes != null) {
225: // encodedBytes = new byte[stream.filteredBytes.length];
226: // System.arraycopy(stream.filteredBytes, 0, encodedBytes, 0,
227: // encodedBytes.length);
228: // }
229: // if (stream.unfilteredBytes != null) {
230: // decodedBytes = new byte[stream.unfilteredBytes.length];
231: // System.arraycopy(stream.unfilteredBytes, 0, decodedBytes, 0,
232: // decodedBytes.length);
233: // }
234: }
235:
236: /*
237: * (non-Javadoc)
238: *
239: * @see de.intarsys.pdf.cos.COSObject#accept(de.intarsys.pdf.cos.ICOSObjectVisitor)
240: */
241: public java.lang.Object accept(ICOSObjectVisitor visitor)
242: throws COSVisitorException {
243: return visitor.visitFromStream(this );
244: }
245:
246: /**
247: * Add a new filter to this.
248: *
249: * @param name
250: * The logical naem of the filter.
251: */
252: public void addFilter(COSName name) {
253: // be sure decoded stream is available
254: getDecodedBytes();
255: encodedBytes = null;
256: COSArray result;
257: COSObject filters = getFilters();
258: if (filters.isNull()) {
259: result = COSArray.create();
260: result.add(name);
261: getDict().put(DK_Filter, result);
262: } else if (filters instanceof COSName) {
263: result = COSArray.create(2);
264: getDict().put(DK_Filter, result);
265: result.add(filters);
266: result.add(name);
267: } else {
268: result = (COSArray) filters;
269: result.add(name);
270: }
271: }
272:
273: /*
274: * (non-Javadoc)
275: *
276: * @see de.intarsys.pdf.cos.COSCompositeObject#addObjectListener(de.intarsys.pdf.cos.ICOSObjectListener)
277: */
278: public void addObjectListener(ICOSObjectListener listener) {
279: super .addObjectListener(listener);
280: if (dict != null) {
281: dict.addObjectListener(listener);
282: }
283: }
284:
285: /*
286: * (non-Javadoc)
287: *
288: * @see de.intarsys.pdf.cos.COSObject#asStream()
289: */
290: public COSStream asStream() {
291: return this ;
292: }
293:
294: /*
295: * (non-Javadoc)
296: *
297: * @see de.intarsys.pdf.cos.COSObject#basicIterator()
298: */
299: public Iterator basicIterator() {
300: return new SingleObjectIterator(getDict());
301: }
302:
303: /**
304: * Set the streams logical content.
305: *
306: * @param newBytes
307: * the logical content for the stream
308: */
309: public void basicSetDecodedBytes(byte[] newBytes) {
310: decodedBytes = newBytes;
311: encodedBytes = null;
312: }
313:
314: /**
315: * Set the streams physical content.
316: *
317: * @param newBytes
318: * the physical content for the stream
319: */
320: public void basicSetEncodedBytes(byte[] newBytes) {
321: encodedBytes = newBytes;
322: decodedBytes = null;
323: }
324:
325: /*
326: * (non-Javadoc)
327: *
328: * @see de.intarsys.pdf.cos.COSObject#basicToString()
329: */
330: protected String basicToString() {
331: return "stream"; //$NON-NLS-1$
332: }
333:
334: /*
335: * (non-Javadoc)
336: *
337: * @see de.intarsys.pdf.cos.COSObject#copyBasic()
338: */
339: protected COSObject copyBasic() {
340: return create(null);
341: }
342:
343: /**
344: * A copy of this, bytes decoded.
345: *
346: * @return A copy of this, bytes decoded.
347: * @throws IOException
348: */
349: public COSStream copyDecodeFirst() throws IOException {
350: COSStream newStream;
351: COSName filter;
352: byte[] bytes;
353:
354: // prepare new stream
355: newStream = (COSStream) copyShallow();
356: if (getFilters().isNull()) {
357: return newStream;
358: }
359: newStream.basicSetDecodedBytes(null);
360: newStream.getDict().remove(DK_Filter);
361: newStream.getDict().remove(DK_Length);
362: // prepare content
363: bytes = getEncodedBytes();
364: if ((filter = getFirstFilter()) != null) {
365: bytes = doDecode(filter, getFirstDecodeParam(), bytes, 0,
366: getAnyLength());
367: }
368: if (getFilters() instanceof COSArray) {
369: for (int index = ((COSArray) getFilters()).size() - 1; index > 0; index--) {
370: newStream.addFilter((COSName) ((COSArray) getFilters())
371: .get(index).copyShallow());
372: }
373: }
374: newStream.setEncodedBytes(bytes);
375: return newStream;
376: }
377:
378: /*
379: * (non-Javadoc)
380: *
381: * @see de.intarsys.pdf.cos.COSCompositeObject#copyDeep(java.util.Map)
382: */
383: public COSObject copyDeep(Map copied) {
384: COSStream result = (COSStream) super .copyDeep(copied);
385: result.setDict((COSDictionary) getDict().copyDeep(copied));
386: if (encodedBytes != null) {
387: result.setEncodedBytes(encodedBytes);
388: } else {
389: result.setDecodedBytes(decodedBytes);
390: }
391: return result;
392: }
393:
394: /*
395: * (non-Javadoc)
396: *
397: * @see de.intarsys.pdf.cos.COSObject#copyShallow()
398: */
399: public COSObject copyShallow() {
400: COSStream result = (COSStream) super .copyShallow();
401: result.setDict((COSDictionary) getDict().copyShallow());
402: if (encodedBytes != null) {
403: result.setEncodedBytes(encodedBytes);
404: } else {
405: result.setDecodedBytes(decodedBytes);
406: }
407: return result;
408: }
409:
410: /*
411: * (non-Javadoc)
412: *
413: * @see de.intarsys.pdf.cos.COSObject#copySubGraph(java.util.Map)
414: */
415: protected COSObject copySubGraph(Map copied) {
416: COSStream result = (COSStream) super .copySubGraph(copied);
417: result.setDict((COSDictionary) getDict().copySubGraph(copied));
418: if (encodedBytes != null) {
419: result.setEncodedBytes(encodedBytes);
420: } else {
421: result.setDecodedBytes(decodedBytes);
422: }
423: return result;
424: }
425:
426: /**
427: * Decode the filtered stream content using the filters defined in the
428: * /Filter entry in the stream dictionary
429: *
430: * @return The decoded bytes.
431: * @throws IOException
432: */
433: protected byte[] doDecode() throws IOException {
434: byte[] newBytes;
435:
436: if (isExternal() && isBytesArrayEmpty(encodedBytes)) {
437: // reset the encodedbytes because of later null checks
438: encodedBytes = null;
439: parseFKeyedFile();
440: }
441: if (encodedBytes == null) {
442: return null;
443: }
444:
445: // get the filters
446: COSObject filters = getFilters();
447: if (filters.isNull()) {
448: int length = getLength();
449: if ((length != -1) && (encodedBytes.length > length)) {
450: newBytes = new byte[length];
451: System.arraycopy(encodedBytes, 0, newBytes, 0, length);
452: } else {
453: newBytes = encodedBytes;
454: }
455: return newBytes;
456: }
457:
458: // get the options
459: COSObject options = getDecodeParams();
460:
461: // decode
462: if (filters instanceof COSName) {
463: newBytes = doDecode((COSName) filters, options
464: .asDictionary(), encodedBytes, 0, getAnyLength());
465: } else {
466: byte[] temp = encodedBytes;
467: int length = getAnyLength();
468: for (int i = 0; i < ((COSArray) filters).size(); i++) {
469: COSObject option = COSNull.NULL;
470: if (!options.isNull()) {
471: option = ((COSArray) options).get(i);
472: }
473: COSName filter = ((COSArray) filters).get(i).asName();
474: temp = doDecode(filter, option.asDictionary(), temp, 0,
475: length);
476: if (temp != null) {
477: length = temp.length;
478: }
479: }
480: newBytes = temp;
481: }
482: return newBytes;
483: }
484:
485: /**
486: * Perform the decoding process of the underlying byte stream.
487: *
488: * @param filterName
489: * The name of a filter to use for this step.
490: * @param options
491: * The options to use for the filter.
492: * @param bytes
493: * The bytes to decode.
494: * @param offset
495: * The offset to start.
496: * @param length
497: * The length to be decoded.
498: *
499: * @return The decoded bytes.
500: *
501: * @throws IOException
502: */
503: protected byte[] doDecode(COSName filterName,
504: COSDictionary options, byte[] bytes, int offset, int length)
505: throws IOException {
506: if (bytes == null) {
507: return new byte[0];
508: }
509: IFilter filter = FilterFactory.get().createFilter(filterName,
510: options);
511: filter.setStream(this );
512: return filter.decode(bytes, offset, length);
513: }
514:
515: /**
516: * encode the filtered stream content using the filters defined in the
517: * /Filter entry in the stream dictionary in reverse order
518: *
519: * @throws IOException
520: */
521: protected void doEncode() throws IOException {
522: if (decodedBytes == null) {
523: return;
524: }
525:
526: // get the filters
527: COSObject filters = getFilters();
528: if (filters.isNull()) {
529: encodedBytes = decodedBytes;
530: return;
531: }
532:
533: // get the options
534: COSObject options = getDecodeParams();
535:
536: // encode
537: if (filters instanceof COSName) {
538: encodedBytes = doEncode((COSName) filters, options
539: .asDictionary(), decodedBytes, 0,
540: decodedBytes.length);
541: } else {
542: byte[] temp = decodedBytes;
543: int length = decodedBytes.length;
544: for (int i = ((COSArray) filters).size() - 1; i >= 0; i--) {
545: COSDictionary option = null;
546: if (!options.isNull()) {
547: option = ((COSArray) options).get(i).asDictionary();
548: }
549:
550: COSName filter = ((COSArray) filters).get(i).asName();
551: temp = doEncode(filter, option, temp, 0, length);
552: length = temp.length;
553: }
554: encodedBytes = temp;
555: }
556: }
557:
558: /**
559: * Perform the encoding process of the underlying byte stream.
560: *
561: * @param filterName
562: * The name of a filter to use for this step.
563: * @param options
564: * The options to use for the filter.
565: * @param bytes
566: * The bytes to encode .
567: * @param offset
568: * The offset to start.
569: * @param length
570: * The length to be encoded.
571: *
572: * @return The encoded bytes.
573: *
574: * @throws IOException
575: */
576: protected byte[] doEncode(COSName filterName,
577: COSDictionary options, byte[] bytes, int offset, int length)
578: throws IOException {
579: if (bytes == null) {
580: return new byte[0];
581: }
582: IFilter filter = FilterFactory.get().createFilter(filterName,
583: options);
584: filter.setStream(this );
585: return filter.encode(bytes, offset, length);
586: }
587:
588: /**
589: * The declared or real length for this.
590: *
591: * @return The declared or real length for this.
592: */
593: public int getAnyLength() {
594: int result = getLength();
595: if (result == -1) {
596: return encodedBytes.length;
597: }
598: return result;
599: }
600:
601: /**
602: * The unfiltered (logical) stream content. It is not intended to manipulate
603: * the byte array directly.
604: *
605: * @return The unfiltered (logical) stream content
606: *
607: * @throws IOException
608: */
609: public byte[] getDecodedBytes() {
610: if (decodedBytes == null) {
611: try {
612: decodedBytes = doDecode();
613: } catch (IOException e) {
614: handleException(new COSRuntimeException(
615: "error decoding stream", e)); //$NON-NLS-1$
616: }
617: }
618: return decodedBytes;
619: }
620:
621: /**
622: * The unfiltered content as in getDecodedBytes, but allow the caller to
623: * manipulate the result by copying/not caching the returned bytes
624: *
625: * @return The unfiltered content as in getDecodedBytes
626: */
627: public byte[] getDecodedBytesWritable() {
628: byte[] bytes;
629: byte[] copiedBytes;
630:
631: if (decodedBytes != null) {
632: bytes = decodedBytes;
633: }
634: try {
635: bytes = doDecode();
636: // take care; doDecode does not always create a new array
637: if (bytes != encodedBytes) {
638: return bytes;
639: }
640: } catch (IOException e) {
641: handleException(new COSRuntimeException(
642: "error decoding stream", e)); //$NON-NLS-1$
643: return new byte[0];
644: }
645: copiedBytes = new byte[bytes.length];
646: System.arraycopy(bytes, 0, copiedBytes, 0, bytes.length);
647: return copiedBytes;
648: }
649:
650: /**
651: * The options or an array of options for filtering.
652: *
653: * @return The options or an array of options for filtering.
654: */
655: public COSObject getDecodeParams() {
656: return getDecodeParams(getDict());
657: }
658:
659: /**
660: * The options corresponding to the first occurence of the filter
661: * <code>name</code>.
662: *
663: * @return The options corresponding to the first occurence of the filter
664: * <code>name</code>.
665: */
666: public COSObject getDecodeParams(COSName name) {
667: return getDecodeParams(getDict(), name);
668: }
669:
670: /**
671: * The stream dictionary
672: *
673: * @return The stream dictionary
674: */
675: public COSDictionary getDict() {
676: return dict;
677: }
678:
679: /**
680: * The filtered (physical) stream content. If it must be generated first,
681: * then the content length is adjusted as a side effect. It is not intended
682: * to manipulate the byte array directly.
683: *
684: * @return The filtered (physical) stream content
685: */
686: public byte[] getEncodedBytes() {
687: if (encodedBytes == null) {
688: try {
689: doEncode();
690: } catch (IOException e) {
691: handleException(new COSRuntimeException(
692: "error encoding stream", e)); //$NON-NLS-1$
693: }
694: int length = (encodedBytes == null) ? 0
695: : encodedBytes.length;
696: getDict().basicPutSilent(DK_Length,
697: COSInteger.create(length));
698: }
699: return encodedBytes;
700: }
701:
702: /**
703: * Return the filter or the collection of filters for the stream.
704: *
705: * @return The filter or the collection of filters for the stream.
706: */
707: public COSObject getFilters() {
708: return getFilters(getDict());
709: }
710:
711: /**
712: * A dictionary with filter options or the first element of an array of such
713: * dictionaries for each filter.
714: *
715: * @return A dictionary with filter options or the first element of an array
716: * of such dictionaries for each filter.
717: */
718: public COSDictionary getFirstDecodeParam() {
719: COSObject dictionaryOrArray;
720:
721: dictionaryOrArray = getDecodeParams();
722: if (dictionaryOrArray.isNull()) {
723: return null;
724: }
725: if (dictionaryOrArray instanceof COSDictionary) {
726: return (COSDictionary) dictionaryOrArray;
727: }
728: if (dictionaryOrArray instanceof COSArray) {
729: return ((COSArray) dictionaryOrArray).get(0).asDictionary();
730: }
731: return null;
732: }
733:
734: /**
735: * The filter or the first element of the collection of filters for the
736: * stream.
737: *
738: * @return The filter or the first element of the collection of filters for
739: * the stream.
740: */
741: public COSName getFirstFilter() {
742: COSObject nameOrArray;
743:
744: nameOrArray = getFilters();
745: if (nameOrArray.isNull()) {
746: return null;
747: }
748: if (nameOrArray instanceof COSName) {
749: return (COSName) nameOrArray;
750: }
751: if (nameOrArray instanceof COSArray) {
752: return ((COSArray) nameOrArray).get(0).asName();
753: }
754: return null;
755: }
756:
757: /**
758: * The length of the filtered content
759: *
760: * @return The length of the filtered content
761: */
762: public int getLength() {
763: COSNumber length = dict.get(DK_Length).asInteger();
764: if (length != null) {
765: return length.intValue();
766: }
767: return -1;
768: }
769:
770: /**
771: * <code>true</code> if the stream hs declared a filter <code>name</code>.
772: *
773: * @param name
774: * The name of the filter.
775: * @return <code>true</code> if the stream hs declared a filter
776: * <code>name</code>.
777: */
778: public boolean hasFilter(COSName name) {
779: return hasFilter(getDict(), name);
780: }
781:
782: /**
783: * tests a byte array for null, length=0 or crlf emptiness
784: *
785: * @param toTest
786: * @return
787: */
788: private boolean isBytesArrayEmpty(byte[] toTest) {
789: if ((toTest == null) || (toTest.length == 0)) {
790: return true;
791: }
792: if ((toTest.length == 2) && (toTest[0] == 13)
793: && (toTest[1] == 10)) {
794: return true;
795: }
796: return false;
797: }
798:
799: /**
800: * <code>true</code> if the stream dictionary contains the F key.
801: *
802: * @return <code>true</code> if the stream dictionary contains the F key.
803: */
804: public boolean isExternal() {
805: return isExternal(getDict());
806: }
807:
808: /*
809: * (non-Javadoc)
810: *
811: * @see de.intarsys.pdf.cos.COSObject#iterator()
812: */
813: public Iterator iterator() {
814: return new SingleObjectIterator(getDict());
815: }
816:
817: /**
818: * Parse the file referenced by the F key in this stream and set as the
819: * filtered content.
820: */
821: protected void parseFKeyedFile() {
822: COSObject fileSpec = dict.get(DK_F);
823: String filepath = ""; //$NON-NLS-1$
824: if (fileSpec instanceof COSString) {
825: filepath = ((COSString) fileSpec).stringValue();
826: } else {
827: // todo 2 implement PDF fileSpecification logic
828: return;
829: }
830: File externalFile = new File(filepath);
831: if (!externalFile.exists()) {
832: return;
833: }
834: byte[] content;
835: try {
836: content = FileTools.toBytes(externalFile);
837: } catch (IOException e) {
838: return;
839: }
840: if (content != null) {
841: encodedBytes = content;
842: }
843: }
844:
845: /**
846: * Remove all filters from this.
847: */
848: public void removeFilters() {
849: // be sure decoded stream is available
850: getDecodedBytes();
851: encodedBytes = null;
852: getDict().remove(DK_Filter);
853: }
854:
855: /*
856: * (non-Javadoc)
857: *
858: * @see de.intarsys.pdf.cos.COSCompositeObject#removeObjectListener(de.intarsys.pdf.cos.ICOSObjectListener)
859: */
860: public void removeObjectListener(ICOSObjectListener listener) {
861: super .removeObjectListener(listener);
862: if (dict != null) {
863: dict.removeObjectListener(listener);
864: }
865: }
866:
867: /*
868: * (non-Javadoc)
869: *
870: * @see de.intarsys.pdf.cos.COSObject#restoreState(java.lang.Object)
871: */
872: public void restoreState(Object object) {
873: super .restoreState(object);
874: COSStream stream = (COSStream) object;
875: encodedBytes = stream.encodedBytes;
876: decodedBytes = stream.decodedBytes;
877: triggerChanged(null, null, null);
878: }
879:
880: /*
881: * (non-Javadoc)
882: *
883: * @see de.intarsys.tools.objectsession.ISaveStateSupport#saveState()
884: */
885: public Object saveState() {
886: return new COSStream(this );
887: }
888:
889: /**
890: * Set the streams logical content
891: *
892: * @param newBytes
893: * The logical content for the stream
894: */
895: public void setDecodedBytes(byte[] newBytes) {
896: willChange(this );
897: basicSetDecodedBytes(newBytes);
898: clearLength();
899: if (objectListeners != null) {
900: triggerChanged(SLOT_BYTES, null, null);
901: }
902: }
903:
904: /**
905: * Give private access to dictionary to ease copying.
906: *
907: * @param dictionary
908: * dictionary part of the stream
909: *
910: * @throws IllegalArgumentException
911: * if the stream is indirect
912: */
913: private void setDict(COSDictionary dictionary) {
914: if (dictionary.isIndirect()) {
915: throw new IllegalArgumentException(
916: "stream dictionary cannot be indirect"); //$NON-NLS-1$
917: }
918: dict = dictionary;
919: dict.addContainer(this );
920: }
921:
922: /**
923: * Set the stream physical content.
924: *
925: * @param newBytes
926: * the physical content for the stream
927: */
928: public void setEncodedBytes(byte[] newBytes) {
929: willChange(this );
930: basicSetEncodedBytes(newBytes);
931: int length = (encodedBytes == null) ? 0 : encodedBytes.length;
932: getDict().basicPutSilent(DK_Length, COSInteger.create(length));
933: if (objectListeners != null) {
934: triggerChanged(SLOT_BYTES, null, null);
935: }
936: }
937:
938: /**
939: * Clear length
940: *
941: */
942: private void clearLength() {
943: getDict().remove(DK_Length);
944: }
945: }
|