001: /*
002: * $Id: Jpeg.java 2839 2007-06-14 15:10:49Z psoares33 $
003: * $Name$
004: *
005: * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
006: *
007: * The contents of this file are subject to the Mozilla Public License Version 1.1
008: * (the "License"); you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the License.
014: *
015: * The Original Code is 'iText, a free JAVA-PDF library'.
016: *
017: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
018: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
019: * All Rights Reserved.
020: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
021: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
022: *
023: * Contributor(s): all the names of the contributors are added in the source code
024: * where applicable.
025: *
026: * Alternatively, the contents of this file may be used under the terms of the
027: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
028: * provisions of LGPL are applicable instead of those above. If you wish to
029: * allow use of your version of this file only under the terms of the LGPL
030: * License and not to allow others to use your version of this file under
031: * the MPL, indicate your decision by deleting the provisions above and
032: * replace them with the notice and other provisions required by the LGPL.
033: * If you do not delete the provisions above, a recipient may use your version
034: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
035: *
036: * This library is free software; you can redistribute it and/or modify it
037: * under the terms of the MPL as stated above or under the terms of the GNU
038: * Library General Public License as published by the Free Software Foundation;
039: * either version 2 of the License, or any later version.
040: *
041: * This library is distributed in the hope that it will be useful, but WITHOUT
042: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
043: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
044: * details.
045: *
046: * If you didn't download this code from the following link, you should check if
047: * you aren't using an obsolete version:
048: * http://www.lowagie.com/iText/
049: */
050:
051: package com.lowagie.text;
052:
053: import java.awt.color.ICC_Profile;
054: import java.io.IOException;
055: import java.io.InputStream;
056: import java.net.URL;
057:
058: /**
059: * An <CODE>Jpeg</CODE> is the representation of a graphic element (JPEG)
060: * that has to be inserted into the document
061: *
062: * @see Element
063: * @see Image
064: */
065:
066: public class Jpeg extends Image {
067:
068: // public static final membervariables
069:
070: /** This is a type of marker. */
071: public static final int NOT_A_MARKER = -1;
072:
073: /** This is a type of marker. */
074: public static final int VALID_MARKER = 0;
075:
076: /** Acceptable Jpeg markers. */
077: public static final int[] VALID_MARKERS = { 0xC0, 0xC1, 0xC2 };
078:
079: /** This is a type of marker. */
080: public static final int UNSUPPORTED_MARKER = 1;
081:
082: /** Unsupported Jpeg markers. */
083: public static final int[] UNSUPPORTED_MARKERS = { 0xC3, 0xC5, 0xC6,
084: 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF };
085:
086: /** This is a type of marker. */
087: public static final int NOPARAM_MARKER = 2;
088:
089: /** Jpeg markers without additional parameters. */
090: public static final int[] NOPARAM_MARKERS = { 0xD0, 0xD1, 0xD2,
091: 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0x01 };
092:
093: /** Marker value */
094: public static final int M_APP0 = 0xE0;
095: /** Marker value */
096: public static final int M_APP2 = 0xE2;
097: /** Marker value */
098: public static final int M_APPE = 0xEE;
099:
100: /** sequence that is used in all Jpeg files */
101: public static final byte JFIF_ID[] = { 0x4A, 0x46, 0x49, 0x46, 0x00 };
102:
103: private byte[][] icc;
104:
105: // Constructors
106:
107: Jpeg(Image image) {
108: super (image);
109: }
110:
111: /**
112: * Constructs a <CODE>Jpeg</CODE>-object, using an <VAR>url</VAR>.
113: *
114: * @param url the <CODE>URL</CODE> where the image can be found
115: * @throws BadElementException
116: * @throws IOException
117: */
118: public Jpeg(URL url) throws BadElementException, IOException {
119: super (url);
120: processParameters();
121: }
122:
123: /**
124: * Constructs a <CODE>Jpeg</CODE>-object from memory.
125: *
126: * @param img the memory image
127: * @throws BadElementException
128: * @throws IOException
129: */
130:
131: public Jpeg(byte[] img) throws BadElementException, IOException {
132: super ((URL) null);
133: rawData = img;
134: originalData = img;
135: processParameters();
136: }
137:
138: /**
139: * Constructs a <CODE>Jpeg</CODE>-object from memory.
140: *
141: * @param img the memory image.
142: * @param width the width you want the image to have
143: * @param height the height you want the image to have
144: * @throws BadElementException
145: * @throws IOException
146: */
147:
148: public Jpeg(byte[] img, float width, float height)
149: throws BadElementException, IOException {
150: this (img);
151: scaledWidth = width;
152: scaledHeight = height;
153: }
154:
155: // private static methods
156:
157: /**
158: * Reads a short from the <CODE>InputStream</CODE>.
159: *
160: * @param is the <CODE>InputStream</CODE>
161: * @return an int
162: * @throws IOException
163: */
164: private static final int getShort(InputStream is)
165: throws IOException {
166: return (is.read() << 8) + is.read();
167: }
168:
169: /**
170: * Returns a type of marker.
171: *
172: * @param marker an int
173: * @return a type: <VAR>VALID_MARKER</CODE>, <VAR>UNSUPPORTED_MARKER</VAR> or <VAR>NOPARAM_MARKER</VAR>
174: */
175: private static final int marker(int marker) {
176: for (int i = 0; i < VALID_MARKERS.length; i++) {
177: if (marker == VALID_MARKERS[i]) {
178: return VALID_MARKER;
179: }
180: }
181: for (int i = 0; i < NOPARAM_MARKERS.length; i++) {
182: if (marker == NOPARAM_MARKERS[i]) {
183: return NOPARAM_MARKER;
184: }
185: }
186: for (int i = 0; i < UNSUPPORTED_MARKERS.length; i++) {
187: if (marker == UNSUPPORTED_MARKERS[i]) {
188: return UNSUPPORTED_MARKER;
189: }
190: }
191: return NOT_A_MARKER;
192: }
193:
194: // private methods
195:
196: /**
197: * This method checks if the image is a valid JPEG and processes some parameters.
198: * @throws BadElementException
199: * @throws IOException
200: */
201: private void processParameters() throws BadElementException,
202: IOException {
203: type = JPEG;
204: originalType = ORIGINAL_JPEG;
205: InputStream is = null;
206: try {
207: String errorID;
208: if (rawData == null) {
209: is = url.openStream();
210: errorID = url.toString();
211: } else {
212: is = new java.io.ByteArrayInputStream(rawData);
213: errorID = "Byte array";
214: }
215: if (is.read() != 0xFF || is.read() != 0xD8) {
216: throw new BadElementException(errorID
217: + " is not a valid JPEG-file.");
218: }
219: boolean firstPass = true;
220: int len;
221: while (true) {
222: int v = is.read();
223: if (v < 0)
224: throw new IOException(
225: "Premature EOF while reading JPG.");
226: if (v == 0xFF) {
227: int marker = is.read();
228: if (firstPass && marker == M_APP0) {
229: firstPass = false;
230: len = getShort(is);
231: if (len < 16) {
232: Utilities.skip(is, len - 2);
233: continue;
234: }
235: byte bcomp[] = new byte[JFIF_ID.length];
236: int r = is.read(bcomp);
237: if (r != bcomp.length)
238: throw new BadElementException(errorID
239: + " corrupted JFIF marker.");
240: boolean found = true;
241: for (int k = 0; k < bcomp.length; ++k) {
242: if (bcomp[k] != JFIF_ID[k]) {
243: found = false;
244: break;
245: }
246: }
247: if (!found) {
248: Utilities.skip(is, len - 2 - bcomp.length);
249: continue;
250: }
251: Utilities.skip(is, 2);
252: int units = is.read();
253: int dx = getShort(is);
254: int dy = getShort(is);
255: if (units == 1) {
256: dpiX = dx;
257: dpiY = dy;
258: } else if (units == 2) {
259: dpiX = (int) ((float) dx * 2.54f + 0.5f);
260: dpiY = (int) ((float) dy * 2.54f + 0.5f);
261: }
262: Utilities.skip(is, len - 2 - bcomp.length - 7);
263: continue;
264: }
265: if (marker == M_APPE) {
266: len = getShort(is) - 2;
267: byte[] byteappe = new byte[len];
268: for (int k = 0; k < len; ++k) {
269: byteappe[k] = (byte) is.read();
270: }
271: if (byteappe.length >= 12) {
272: String appe = new String(byteappe, 0, 5,
273: "ISO-8859-1");
274: if (appe.equals("Adobe")) {
275: invert = true;
276: }
277: }
278: continue;
279: }
280: if (marker == M_APP2) {
281: len = getShort(is) - 2;
282: byte[] byteapp2 = new byte[len];
283: for (int k = 0; k < len; ++k) {
284: byteapp2[k] = (byte) is.read();
285: }
286: if (byteapp2.length >= 14) {
287: String app2 = new String(byteapp2, 0, 11,
288: "ISO-8859-1");
289: if (app2.equals("ICC_PROFILE")) {
290: int order = byteapp2[12] & 0xff;
291: int count = byteapp2[13] & 0xff;
292: if (icc == null)
293: icc = new byte[count][];
294: icc[order - 1] = byteapp2;
295: }
296: }
297: continue;
298: }
299: firstPass = false;
300: int markertype = marker(marker);
301: if (markertype == VALID_MARKER) {
302: Utilities.skip(is, 2);
303: if (is.read() != 0x08) {
304: throw new BadElementException(
305: errorID
306: + " must have 8 bits per component.");
307: }
308: scaledHeight = getShort(is);
309: setTop(scaledHeight);
310: scaledWidth = getShort(is);
311: setRight(scaledWidth);
312: colorspace = is.read();
313: bpc = 8;
314: break;
315: } else if (markertype == UNSUPPORTED_MARKER) {
316: throw new BadElementException(errorID
317: + ": unsupported JPEG marker: "
318: + marker);
319: } else if (markertype != NOPARAM_MARKER) {
320: Utilities.skip(is, getShort(is) - 2);
321: }
322: }
323: }
324: } finally {
325: if (is != null) {
326: is.close();
327: }
328: }
329: plainWidth = getWidth();
330: plainHeight = getHeight();
331: if (icc != null) {
332: int total = 0;
333: for (int k = 0; k < icc.length; ++k) {
334: if (icc[k] == null) {
335: icc = null;
336: return;
337: }
338: total += icc[k].length - 14;
339: }
340: byte[] ficc = new byte[total];
341: total = 0;
342: for (int k = 0; k < icc.length; ++k) {
343: System.arraycopy(icc[k], 14, ficc, total,
344: icc[k].length - 14);
345: total += icc[k].length - 14;
346: }
347: ICC_Profile icc_prof = ICC_Profile.getInstance(ficc);
348: tagICC(icc_prof);
349: icc = null;
350: }
351: }
352: }
|