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.

JpegImage.java 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.image;
  18. // Java
  19. import java.io.ByteArrayOutputStream;
  20. import java.awt.color.ColorSpace;
  21. import java.awt.color.ICC_Profile;
  22. // FOP
  23. import org.apache.avalon.framework.logger.Logger;
  24. import org.apache.fop.util.CMYKColorSpace;
  25. /**
  26. * FopImage object for JPEG images, Using Java native classes.
  27. * @author Eric Dalquist
  28. * @see AbstractFopImage
  29. * @see FopImage
  30. */
  31. public class JpegImage extends AbstractFopImage {
  32. private ICC_Profile iccProfile = null;
  33. private boolean foundICCProfile = false;
  34. private boolean foundDimensions = false;
  35. /**
  36. * Create a jpeg image with the info.
  37. *
  38. * @param imgInfo the image info for this jpeg
  39. */
  40. public JpegImage(FopImage.ImageInfo imgInfo) {
  41. super(imgInfo);
  42. }
  43. /**
  44. * Load the original jpeg data.
  45. * This loads the original jpeg data and reads the color space,
  46. * and icc profile if any.
  47. *
  48. * @param ua the user agent
  49. * @return true if loaded false for any error
  50. */
  51. protected boolean loadOriginalData(Logger logger) {
  52. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  53. ByteArrayOutputStream iccStream = new ByteArrayOutputStream();
  54. int index = 0;
  55. boolean cont = true;
  56. try {
  57. byte[] readBuf = new byte[4096];
  58. int bytesRead;
  59. while ((bytesRead = inputStream.read(readBuf)) != -1) {
  60. baos.write(readBuf, 0, bytesRead);
  61. }
  62. inputStream.close();
  63. inputStream = null;
  64. } catch (java.io.IOException ex) {
  65. logger.error("Error while loading image "
  66. + " : " + ex.getClass()
  67. + " - " + ex.getMessage(), ex);
  68. return false;
  69. }
  70. this.bitmaps = baos.toByteArray();
  71. this.bitsPerPixel = 8;
  72. this.isTransparent = false;
  73. //Check for SOI (Start of image) marker (FFD8)
  74. if (this.bitmaps.length > (index + 2)
  75. && uByte(this.bitmaps[index]) == 255 /*0xFF*/
  76. && uByte(this.bitmaps[index + 1]) == 216 /*0xD8*/) {
  77. index += 2;
  78. while (index < this.bitmaps.length && cont) {
  79. //check to be sure this is the begining of a header
  80. if (this.bitmaps.length > (index + 2)
  81. && uByte(this.bitmaps[index]) == 255 /*0xFF*/) {
  82. //192 or 194 are the header bytes that contain
  83. // the jpeg width height and color depth.
  84. if (uByte(this.bitmaps[index + 1]) == 192 /*0xC0*/
  85. || uByte(this.bitmaps[index + 1]) == 194 /*0xC2*/) {
  86. this.height = calcBytes(this.bitmaps[index + 5],
  87. this.bitmaps[index + 6]);
  88. this.width = calcBytes(this.bitmaps[index + 7],
  89. this.bitmaps[index + 8]);
  90. if (this.bitmaps[index + 9] == 1) {
  91. this.colorSpace = ColorSpace.getInstance(
  92. ColorSpace.CS_GRAY);
  93. } else if (this.bitmaps[index + 9] == 3) {
  94. this.colorSpace = ColorSpace.getInstance(
  95. ColorSpace.CS_LINEAR_RGB);
  96. } else if (this.bitmaps[index + 9] == 4) {
  97. // howto create CMYK color space
  98. /*
  99. this.colorSpace = ColorSpace.getInstance(
  100. ColorSpace.CS_CIEXYZ);
  101. */
  102. this.colorSpace = CMYKColorSpace.getInstance();
  103. } else {
  104. logger.error("Unknown ColorSpace for image: "
  105. + "");
  106. return false;
  107. }
  108. foundDimensions = true;
  109. if (foundICCProfile) {
  110. cont = false;
  111. break;
  112. }
  113. index += calcBytes(this.bitmaps[index + 2],
  114. this.bitmaps[index + 3]) + 2;
  115. } else if (uByte(this.bitmaps[index + 1]) == 226 /*0xE2*/
  116. && this.bitmaps.length > (index + 60)) {
  117. // Check if ICC profile
  118. byte[] iccString = new byte[11];
  119. System.arraycopy(this.bitmaps, index + 4,
  120. iccString, 0, 11);
  121. if ("ICC_PROFILE".equals(new String(iccString))) {
  122. int chunkSize = calcBytes(
  123. this.bitmaps[index + 2],
  124. this.bitmaps[index + 3]) + 2;
  125. iccStream.write(this.bitmaps,
  126. index + 18, chunkSize - 18);
  127. }
  128. index += calcBytes(this.bitmaps[index + 2],
  129. this.bitmaps[index + 3]) + 2;
  130. } else {
  131. index += calcBytes(this.bitmaps[index + 2],
  132. this.bitmaps[index + 3]) + 2;
  133. }
  134. } else {
  135. cont = false;
  136. }
  137. }
  138. } else {
  139. logger.error("Error while loading "
  140. + "JpegImage - Invalid JPEG Header.");
  141. return false;
  142. }
  143. if (iccStream.size() > 0) {
  144. byte[] align = new byte[((iccStream.size()) % 8) + 8];
  145. try {
  146. iccStream.write(align);
  147. } catch (Exception e) {
  148. logger.error("Error while loading image "
  149. + " : "
  150. + e.getMessage(), e);
  151. return false;
  152. }
  153. try {
  154. iccProfile = ICC_Profile.getInstance(iccStream.toByteArray());
  155. } catch (Exception e) {
  156. logger.error("Invalid ICC profile: " + e, e);
  157. return false;
  158. }
  159. } else if (this.colorSpace == null) {
  160. logger.error("ColorSpace not specified for JPEG image");
  161. return false;
  162. }
  163. return true;
  164. }
  165. /**
  166. * Get the ICC profile for this Jpeg image.
  167. *
  168. * @return the icc profile or null if not found
  169. */
  170. public ICC_Profile getICCProfile() {
  171. return iccProfile;
  172. }
  173. private int calcBytes(byte bOne, byte bTwo) {
  174. return (uByte(bOne) * 256) + uByte(bTwo);
  175. }
  176. private int uByte(byte bIn) {
  177. if (bIn < 0) {
  178. return 256 + bIn;
  179. } else {
  180. return bIn;
  181. }
  182. }
  183. }