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.

PDFFilterList.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  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.util.List;
  22. import java.util.Map;
  23. /**
  24. * This class represents a list of PDF filters to be applied when serializing
  25. * the output of a PDF object.
  26. */
  27. public class PDFFilterList {
  28. /** Key for the default filter */
  29. public static final String DEFAULT_FILTER = "default";
  30. /** Key for the filter used for normal content*/
  31. public static final String CONTENT_FILTER = "content";
  32. /** Key for the filter used for precompressed content */
  33. public static final String PRECOMPRESSED_FILTER = "precompressed";
  34. /** Key for the filter used for images */
  35. public static final String IMAGE_FILTER = "image";
  36. /** Key for the filter used for JPEG images */
  37. public static final String JPEG_FILTER = "jpeg";
  38. /** Key for the filter used for TIFF images */
  39. public static final String TIFF_FILTER = "tiff";
  40. /** Key for the filter used for fonts */
  41. public static final String FONT_FILTER = "font";
  42. /** Key for the filter used for metadata */
  43. public static final String METADATA_FILTER = "metadata";
  44. private List filters = new java.util.ArrayList();
  45. private boolean ignoreASCIIFilters = false;
  46. private boolean disableAllFilters = false;
  47. /**
  48. * Default constructor.
  49. * <p>
  50. * The flag for ignoring ASCII filters defaults to false.
  51. */
  52. public PDFFilterList() {
  53. //nop
  54. }
  55. /**
  56. * Use this descriptor if you want to have ASCII filters (such as ASCIIHex
  57. * and ASCII85) ignored, for example, when encryption is active.
  58. * @param ignoreASCIIFilters true if ASCII filters should be ignored
  59. */
  60. public PDFFilterList(boolean ignoreASCIIFilters) {
  61. this.ignoreASCIIFilters = ignoreASCIIFilters;
  62. }
  63. /**
  64. * Used to disable all filters.
  65. * @param value true if all filters shall be disabled
  66. */
  67. public void setDisableAllFilters(boolean value) {
  68. this.disableAllFilters = value;
  69. }
  70. /**
  71. * Returns true if all filters are disabled.
  72. * @return true if all filters are disabled
  73. */
  74. public boolean isDisableAllFilters() {
  75. return this.disableAllFilters;
  76. }
  77. /**
  78. * Indicates whether the filter list is already initialized.
  79. * @return true if more there are filters present
  80. */
  81. public boolean isInitialized() {
  82. return this.filters.size() > 0;
  83. }
  84. /**
  85. * Add a filter for compression of the stream. Filters are
  86. * applied in the order they are added. This should always be a
  87. * new instance of the particular filter of choice. The applied
  88. * flag in the filter is marked true after it has been applied to the
  89. * data.
  90. * @param filter filter to add
  91. */
  92. public void addFilter(PDFFilter filter) {
  93. if (filter != null) {
  94. if (this.ignoreASCIIFilters && filter.isASCIIFilter()) {
  95. return; //ignore ASCII filter
  96. }
  97. filters.add(filter);
  98. }
  99. }
  100. /**
  101. * Add a filter for compression of the stream by name.
  102. * @param filterType name of the filter to add
  103. */
  104. public void addFilter(String filterType) {
  105. if (filterType == null) {
  106. return;
  107. }
  108. if (filterType.equals("flate")) {
  109. addFilter(new FlateFilter());
  110. } else if (filterType.equals("null")) {
  111. addFilter(new NullFilter());
  112. } else if (filterType.equals("ascii-85")) {
  113. if (this.ignoreASCIIFilters) {
  114. return; //ignore ASCII filter
  115. }
  116. addFilter(new ASCII85Filter());
  117. } else if (filterType.equals("ascii-hex")) {
  118. if (this.ignoreASCIIFilters) {
  119. return; //ignore ASCII filter
  120. }
  121. addFilter(new ASCIIHexFilter());
  122. } else if (filterType.equals("")) {
  123. return;
  124. } else {
  125. throw new IllegalArgumentException(
  126. "Unsupported filter type in stream-filter-list: " + filterType);
  127. }
  128. }
  129. /**
  130. * Checks the filter list for the filter and adds it in the correct
  131. * place if necessary.
  132. * @param pdfFilter the filter to check / add
  133. */
  134. public void ensureFilterInPlace(PDFFilter pdfFilter) {
  135. if (this.filters.size() == 0) {
  136. addFilter(pdfFilter);
  137. } else {
  138. if (!(this.filters.get(0).equals(pdfFilter))) {
  139. this.filters.add(0, pdfFilter);
  140. }
  141. }
  142. }
  143. /**
  144. * Adds the default filters to this stream.
  145. * @param filters Map of filters
  146. * @param type which filter list to modify
  147. */
  148. public void addDefaultFilters(Map filters, String type) {
  149. if (METADATA_FILTER.equals(type)) {
  150. //XMP metadata should not be embedded in clear-text
  151. addFilter(new NullFilter());
  152. return;
  153. }
  154. List filterset = null;
  155. if (filters != null) {
  156. filterset = (List)filters.get(type);
  157. if (filterset == null) {
  158. filterset = (List)filters.get(DEFAULT_FILTER);
  159. }
  160. }
  161. if (filterset == null || filterset.size() == 0) {
  162. if (JPEG_FILTER.equals(type)) {
  163. //JPEG is already well compressed
  164. addFilter(new NullFilter());
  165. } else if (TIFF_FILTER.equals(type)) {
  166. //CCITT-encoded images are already well compressed
  167. addFilter(new NullFilter());
  168. } else if (PRECOMPRESSED_FILTER.equals(type)) {
  169. //precompressed content doesn't need further compression
  170. addFilter(new NullFilter());
  171. } else {
  172. // built-in default to flate
  173. addFilter(new FlateFilter());
  174. }
  175. } else {
  176. for (int i = 0; i < filterset.size(); i++) {
  177. String v = (String)filterset.get(i);
  178. addFilter(v);
  179. }
  180. }
  181. }
  182. /**
  183. * Apply the filters to the data
  184. * in the order given and return the /Filter and /DecodeParms
  185. * entries for the stream dictionary. If the filters have already
  186. * been applied to the data (either externally, or internally)
  187. * then the dictionary entries are built and returned.
  188. * @return a String representing the filter list
  189. */
  190. protected String buildFilterDictEntries() {
  191. if (filters != null && filters.size() > 0) {
  192. List names = new java.util.ArrayList();
  193. List parms = new java.util.ArrayList();
  194. int nonNullParams = populateNamesAndParms(names, parms);
  195. // now build up the filter entries for the dictionary
  196. return buildFilterEntries(names)
  197. + (nonNullParams > 0 ? buildDecodeParms(parms) : "");
  198. }
  199. return "";
  200. }
  201. /**
  202. * Apply the filters to the data
  203. * in the order given and add the /Filter and /DecodeParms
  204. * entries to the stream dictionary. If the filters have already
  205. * been applied to the data (either externally, or internally)
  206. * then the dictionary entries added.
  207. * @param dict the PDFDictionary to set the entries on
  208. */
  209. protected void putFilterDictEntries(PDFDictionary dict) {
  210. if (filters != null && filters.size() > 0) {
  211. List names = new java.util.ArrayList();
  212. List parms = new java.util.ArrayList();
  213. populateNamesAndParms(names, parms);
  214. // now build up the filter entries for the dictionary
  215. putFilterEntries(dict, names);
  216. putDecodeParams(dict, parms);
  217. }
  218. }
  219. private int populateNamesAndParms(List names, List parms) {
  220. // run the filters
  221. int nonNullParams = 0;
  222. for (int count = 0; count < filters.size(); count++) {
  223. PDFFilter filter = (PDFFilter)filters.get(count);
  224. // place the names in our local vector in reverse order
  225. if (filter.getName().length() > 0) {
  226. names.add(0, filter.getName());
  227. PDFObject param = filter.getDecodeParms();
  228. if (param != null) {
  229. parms.add(0, param);
  230. nonNullParams++;
  231. } else {
  232. parms.add(0, null);
  233. }
  234. }
  235. }
  236. return nonNullParams;
  237. }
  238. private String buildFilterEntries(List names) {
  239. int filterCount = 0;
  240. StringBuffer sb = new StringBuffer(64);
  241. for (int i = 0; i < names.size(); i++) {
  242. final String name = (String)names.get(i);
  243. if (name.length() > 0) {
  244. filterCount++;
  245. sb.append(name);
  246. sb.append(" ");
  247. }
  248. }
  249. if (filterCount > 0) {
  250. if (filterCount > 1) {
  251. return "/Filter [ " + sb.toString() + "]";
  252. } else {
  253. return "/Filter " + sb.toString();
  254. }
  255. } else {
  256. return "";
  257. }
  258. }
  259. private void putFilterEntries(PDFDictionary dict, List names) {
  260. PDFArray array = new PDFArray(dict);
  261. for (int i = 0, c = names.size(); i < c; i++) {
  262. final String name = (String)names.get(i);
  263. if (name.length() > 0) {
  264. array.add(new PDFName(name));
  265. }
  266. }
  267. if (array.length() > 0) {
  268. if (array.length() > 1) {
  269. dict.put("Filter", array);
  270. } else {
  271. dict.put("Filter", array.get(0));
  272. }
  273. }
  274. }
  275. private String buildDecodeParms(List parms) {
  276. StringBuffer sb = new StringBuffer();
  277. boolean needParmsEntry = false;
  278. sb.append("\n/DecodeParms ");
  279. if (parms.size() > 1) {
  280. sb.append("[ ");
  281. }
  282. for (int count = 0; count < parms.size(); count++) {
  283. String s = (String)parms.get(count);
  284. if (s != null) {
  285. sb.append(s);
  286. needParmsEntry = true;
  287. } else {
  288. sb.append("null");
  289. }
  290. sb.append(" ");
  291. }
  292. if (parms.size() > 1) {
  293. sb.append("]");
  294. }
  295. if (needParmsEntry) {
  296. return sb.toString();
  297. } else {
  298. return "";
  299. }
  300. }
  301. private void putDecodeParams(PDFDictionary dict, List parms) {
  302. boolean needParmsEntry = false;
  303. PDFArray array = new PDFArray(dict);
  304. for (int i = 0, c = parms.size(); i < c; i++) {
  305. Object obj = parms.get(i);
  306. if (obj != null) {
  307. array.add(obj);
  308. needParmsEntry = true;
  309. } else {
  310. array.add(null);
  311. }
  312. }
  313. if (array.length() > 0 & needParmsEntry) {
  314. if (array.length() > 1) {
  315. dict.put("DecodeParms", array);
  316. } else {
  317. dict.put("DecodeParms", array.get(0));
  318. }
  319. }
  320. }
  321. /**
  322. * Applies all registered filters as necessary. The method returns an
  323. * OutputStream which will receive the filtered contents.
  324. * @param stream raw data output stream
  325. * @return OutputStream filtered output stream
  326. * @throws IOException In case of an I/O problem
  327. */
  328. public OutputStream applyFilters(OutputStream stream) throws IOException {
  329. OutputStream out = stream;
  330. if (filters != null && !isDisableAllFilters()) {
  331. for (int count = filters.size() - 1; count >= 0; count--) {
  332. PDFFilter filter = (PDFFilter)filters.get(count);
  333. out = filter.applyFilter(out);
  334. }
  335. }
  336. return out;
  337. }
  338. }