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 8.2KB

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