You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractPDFStream.java 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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. /* $Id$ */
  18. package org.apache.fop.pdf;
  19. import java.io.IOException;
  20. import java.io.OutputStream;
  21. import java.io.Writer;
  22. import org.apache.commons.io.output.CountingOutputStream;
  23. import org.apache.fop.util.CloseBlockerOutputStream;
  24. /**
  25. * This is an abstract base class for PDF streams.
  26. */
  27. public abstract class AbstractPDFStream extends PDFDictionary {
  28. /** The filters that should be applied */
  29. private PDFFilterList filters;
  30. /**
  31. * Constructor for AbstractPDFStream.
  32. */
  33. public AbstractPDFStream() {
  34. super();
  35. }
  36. /**
  37. * Sets up the default filters for this stream if they haven't been set
  38. * from outside.
  39. */
  40. protected void setupFilterList() {
  41. if (!getFilterList().isInitialized()) {
  42. getFilterList().addDefaultFilters(
  43. getDocumentSafely().getFilterMap(),
  44. PDFFilterList.DEFAULT_FILTER);
  45. }
  46. prepareImplicitFilters();
  47. getDocument().applyEncryption(this);
  48. }
  49. /**
  50. * Returns the associated filter list.
  51. * @return the filter list
  52. */
  53. public PDFFilterList getFilterList() {
  54. if (this.filters == null) {
  55. if (getDocument() == null) {
  56. this.filters = new PDFFilterList();
  57. } else {
  58. this.filters = new PDFFilterList(getDocument().isEncryptionActive());
  59. }
  60. boolean hasFilterEntries = (get("Filter") != null);
  61. if (hasFilterEntries) {
  62. this.filters.setDisableAllFilters(true);
  63. }
  64. }
  65. return this.filters;
  66. }
  67. /**
  68. * Returns a value that hints at the size of the encoded stream. This is
  69. * used to optimize buffer allocation so fewer buffer reallocations are
  70. * necessary.
  71. * @return an estimated size (0 if no hint can be given)
  72. * @throws IOException in case of an I/O problem
  73. */
  74. protected abstract int getSizeHint() throws IOException;
  75. /**
  76. * Sends the raw stream data to the target OutputStream.
  77. * @param out OutputStream to write to
  78. * @throws IOException In case of an I/O problem
  79. */
  80. protected abstract void outputRawStreamData(OutputStream out)
  81. throws IOException;
  82. /**
  83. * Output just the stream data enclosed by stream/endstream markers
  84. * @param encodedStream already encoded/filtered stream to write
  85. * @param out OutputStream to write to
  86. * @return int number of bytes written
  87. * @throws IOException in case of an I/O problem
  88. */
  89. protected int outputStreamData(StreamCache encodedStream, OutputStream out) throws IOException {
  90. int length = 0;
  91. byte[] p = encode("stream\n");
  92. out.write(p);
  93. length += p.length;
  94. encodedStream.outputContents(out);
  95. length += encodedStream.getSize();
  96. p = encode("\nendstream");
  97. out.write(p);
  98. length += p.length;
  99. return length;
  100. }
  101. /**
  102. * Encodes the raw data stream for output to a PDF file.
  103. * @return the encoded stream
  104. * @throws IOException in case of an I/O problem
  105. */
  106. protected StreamCache encodeStream() throws IOException {
  107. //Allocate a temporary buffer to find out the size of the encoded stream
  108. final StreamCache encodedStream = StreamCacheFactory.getInstance().
  109. createStreamCache(getSizeHint());
  110. OutputStream filteredOutput
  111. = getFilterList().applyFilters(encodedStream.getOutputStream());
  112. outputRawStreamData(filteredOutput);
  113. filteredOutput.flush();
  114. filteredOutput.close();
  115. return encodedStream;
  116. }
  117. /**
  118. * Encodes and writes a stream directly to an OutputStream. The length of
  119. * the stream, in this case, is set on a PDFNumber object that has to be
  120. * prepared beforehand.
  121. * @param out OutputStream to write to
  122. * @param refLength PDFNumber object to receive the stream length
  123. * @return number of bytes written (header and trailer included)
  124. * @throws IOException in case of an I/O problem
  125. */
  126. protected int encodeAndWriteStream(OutputStream out, PDFNumber refLength)
  127. throws IOException {
  128. int bytesWritten = 0;
  129. //Stream header
  130. byte[] buf = encode("stream\n");
  131. out.write(buf);
  132. bytesWritten += buf.length;
  133. //Stream contents
  134. CloseBlockerOutputStream cbout = new CloseBlockerOutputStream(out);
  135. CountingOutputStream cout = new CountingOutputStream(cbout);
  136. OutputStream filteredOutput = getFilterList().applyFilters(cout);
  137. outputRawStreamData(filteredOutput);
  138. filteredOutput.close();
  139. refLength.setNumber(new Integer(cout.getCount()));
  140. bytesWritten += cout.getCount();
  141. //Stream trailer
  142. buf = encode("\nendstream");
  143. out.write(buf);
  144. bytesWritten += buf.length;
  145. return bytesWritten;
  146. }
  147. /**
  148. * Overload the base object method so we don't have to copy
  149. * byte arrays around so much
  150. * {@inheritDoc}
  151. */
  152. protected int output(OutputStream stream) throws IOException {
  153. setupFilterList();
  154. CountingOutputStream cout = new CountingOutputStream(stream);
  155. Writer writer = PDFDocument.getWriterFor(cout);
  156. writer.write(getObjectID());
  157. //int length = 0;
  158. StreamCache encodedStream = null;
  159. PDFNumber refLength = null;
  160. final Object lengthEntry;
  161. if (getDocument().isEncodingOnTheFly()) {
  162. refLength = new PDFNumber();
  163. getDocumentSafely().registerObject(refLength);
  164. lengthEntry = refLength;
  165. } else {
  166. encodedStream = encodeStream();
  167. lengthEntry = new Integer(encodedStream.getSize() + 1);
  168. }
  169. populateStreamDict(lengthEntry);
  170. writeDictionary(cout, writer);
  171. //Send encoded stream to target OutputStream
  172. writer.flush();
  173. if (encodedStream == null) {
  174. encodeAndWriteStream(cout, refLength);
  175. } else {
  176. outputStreamData(encodedStream, cout);
  177. encodedStream.clear(); //Encoded stream can now be discarded
  178. }
  179. writer.write("\nendobj\n");
  180. writer.flush();
  181. return cout.getCount();
  182. }
  183. /**
  184. * Populates the dictionary with all necessary entries for the stream.
  185. * Override this method if you need additional entries.
  186. * @param lengthEntry value for the /Length entry
  187. */
  188. protected void populateStreamDict(Object lengthEntry) {
  189. put("Length", lengthEntry);
  190. if (!getFilterList().isDisableAllFilters()) {
  191. getFilterList().putFilterDictEntries(this);
  192. }
  193. }
  194. /**
  195. * Prepares implicit filters (such as the DCTFilter for JPEG images). You
  196. * must make sure that the appropriate filters are in the filter list at
  197. * the right places.
  198. */
  199. protected void prepareImplicitFilters() {
  200. //nop: No default implicit filters
  201. }
  202. }