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.

ImageRawJPEGAdapter.java 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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.render.pdf;
  19. import java.awt.color.ICC_Profile;
  20. import java.io.DataInput;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.OutputStream;
  24. import org.apache.commons.io.IOUtils;
  25. import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
  26. import org.apache.xmlgraphics.image.loader.impl.JPEGConstants;
  27. import org.apache.xmlgraphics.image.loader.impl.JPEGFile;
  28. import org.apache.xmlgraphics.image.loader.util.ImageUtil;
  29. import org.apache.fop.pdf.DCTFilter;
  30. import org.apache.fop.pdf.PDFDeviceColorSpace;
  31. import org.apache.fop.pdf.PDFDocument;
  32. import org.apache.fop.pdf.PDFFilter;
  33. import org.apache.fop.pdf.PDFFilterList;
  34. import org.apache.fop.util.ColorProfileUtil;
  35. /**
  36. * PDFImage implementation for the PDF renderer which handles raw JPEG images.
  37. * <p>
  38. * The JPEG is copied to the XObject's stream as-is but some elements (marker segments) are
  39. * filtered. For example, an embedded color profile is filtered since it is already added as
  40. * a PDF object and associated with the XObject. This way, the PDF file size is kept as small
  41. * as possible.
  42. */
  43. public class ImageRawJPEGAdapter extends AbstractImageAdapter {
  44. private PDFFilter pdfFilter = null;
  45. /**
  46. * Creates a new PDFImage from an Image instance.
  47. * @param image the JPEG image
  48. * @param key XObject key
  49. */
  50. public ImageRawJPEGAdapter(ImageRawJPEG image, String key) {
  51. super(image, key);
  52. }
  53. /**
  54. * Returns the {@link ImageRawJPEG} instance for this adapter.
  55. * @return the image instance
  56. */
  57. public ImageRawJPEG getImage() {
  58. return ((ImageRawJPEG)this.image);
  59. }
  60. /** {@inheritDoc} */
  61. public void setup(PDFDocument doc) {
  62. pdfFilter = new DCTFilter();
  63. pdfFilter.setApplied(true);
  64. super.setup(doc);
  65. }
  66. /** {@inheritDoc} */
  67. public PDFDeviceColorSpace getColorSpace() {
  68. // DeviceGray, DeviceRGB, or DeviceCMYK
  69. return toPDFColorSpace(getImageColorSpace());
  70. }
  71. /** {@inheritDoc} */
  72. protected ICC_Profile getEffectiveICCProfile() {
  73. ICC_Profile profile = super.getEffectiveICCProfile();
  74. if (profile != null
  75. && profile.getNumComponents() == 3
  76. && !ColorProfileUtil.isDefaultsRGB(profile)) {
  77. //RGB profiles which are not sRGB don't seem to work.
  78. //Without this override, the image drifts into yellow for an unknown reason.
  79. //TODO Find out why this happens.
  80. //Test using a JPEG images with, for example, "Adobe RGB 1998" color profile.
  81. profile = null;
  82. }
  83. return profile;
  84. }
  85. /** {@inheritDoc} */
  86. public int getBitsPerComponent() {
  87. return 8;
  88. }
  89. /** @return true for CMYK images generated by Adobe Photoshop */
  90. public boolean isInverted() {
  91. return getImage().isInverted();
  92. }
  93. /** {@inheritDoc} */
  94. public PDFFilter getPDFFilter() {
  95. return pdfFilter;
  96. }
  97. /** {@inheritDoc} */
  98. public void outputContents(OutputStream out) throws IOException {
  99. InputStream in = getImage().createInputStream();
  100. in = ImageUtil.decorateMarkSupported(in);
  101. try {
  102. JPEGFile jpeg = new JPEGFile(in);
  103. DataInput din = jpeg.getDataInput();
  104. //Copy the whole JPEG file except:
  105. // - the ICC profile
  106. //TODO Thumbnails could safely be skipped, too.
  107. //TODO Metadata (XMP, IPTC, EXIF) could safely be skipped, too.
  108. while (true) {
  109. int reclen;
  110. int segID = jpeg.readMarkerSegment();
  111. switch (segID) {
  112. case JPEGConstants.SOI:
  113. out.write(0xFF);
  114. out.write(segID);
  115. break;
  116. case JPEGConstants.EOI:
  117. case JPEGConstants.SOS:
  118. out.write(0xFF);
  119. out.write(segID);
  120. IOUtils.copy(in, out); //Just copy the rest!
  121. return;
  122. /*
  123. case JPEGConstants.APP1: //Metadata
  124. case JPEGConstants.APPD:
  125. jpeg.skipCurrentMarkerSegment();
  126. break;*/
  127. case JPEGConstants.APP2: //ICC (see ICC1V42.pdf)
  128. boolean skipICCProfile = false;
  129. in.mark(16);
  130. try {
  131. reclen = jpeg.readSegmentLength();
  132. // Check for ICC profile
  133. byte[] iccString = new byte[11];
  134. din.readFully(iccString);
  135. din.skipBytes(1); //string terminator (null byte)
  136. if ("ICC_PROFILE".equals(new String(iccString, "US-ASCII"))) {
  137. skipICCProfile = (this.image.getICCProfile() != null);
  138. }
  139. } finally {
  140. in.reset();
  141. }
  142. if (skipICCProfile) {
  143. //ICC profile is skipped as it is already embedded as a PDF object
  144. jpeg.skipCurrentMarkerSegment();
  145. break;
  146. }
  147. default:
  148. out.write(0xFF);
  149. out.write(segID);
  150. reclen = jpeg.readSegmentLength();
  151. //write short
  152. out.write((reclen >>> 8) & 0xFF);
  153. out.write((reclen >>> 0) & 0xFF);
  154. int left = reclen - 2;
  155. byte[] buf = new byte[2048];
  156. while (left > 0) {
  157. int part = Math.min(buf.length, left);
  158. din.readFully(buf, 0, part);
  159. out.write(buf, 0, part);
  160. left -= part;
  161. }
  162. }
  163. }
  164. } finally {
  165. IOUtils.closeQuietly(in);
  166. }
  167. }
  168. /** {@inheritDoc} */
  169. public String getFilterHint() {
  170. return PDFFilterList.JPEG_FILTER;
  171. }
  172. }