001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2008
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.core;
034:
035: import com.flexive.shared.FxSharedUtils;
036: import com.flexive.shared.exceptions.FxApplicationException;
037:
038: import javax.xml.stream.XMLOutputFactory;
039: import javax.xml.stream.XMLStreamException;
040: import javax.xml.stream.XMLStreamWriter;
041: import java.io.*;
042: import java.util.regex.Pattern;
043:
044: /**
045: * ImageMagick identify parser
046: *
047: * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
048: */
049: public class IMParser {
050:
051: // Name of the identify executeable
052: public final static String IDENTIFY_BINARY = "identify";
053: // Name of the convert executeable
054: public final static String CONVERT_BINARY = "convert";
055:
056: /**
057: * Get the identation depth of the current line (2 characters = 1 level)
058: *
059: * @param data line to examine
060: * @return identation depth
061: */
062: private static int getLevel(String data) {
063: if (data == null || data.length() == 0)
064: return 0;
065: int ident = 0;
066: for (int i = 0; i < data.length(); i++) {
067: if (data.charAt(i) != ' ')
068: return ident / 2;
069: ident++;
070: }
071: if (ident == 0)
072: return ident;
073: return ident / 2;
074: }
075:
076: /**
077: * Parse a ping response from ImageMagick for image dimensions
078: *
079: * @param extension extension of the file
080: * @param line the response from ImageMagick's ping command
081: * @return array containing dimensions or {0,0} if an error occured
082: */
083: public static int[] getPingDimensions(String extension, String line) {
084: try {
085: int start = 0;
086: if (extension.equals(".JPG"))
087: start = line.indexOf(" JPEG ") + 1;
088: if (start <= 0 && extension.equals(".PNG"))
089: start = line.indexOf(" PNG ") + 1;
090: if (start <= 0 && extension.equals(".GIF"))
091: start = line.indexOf(" GIF ") + 1;
092: if (start <= 0) {
093: String[] tmp = line.split(" ");
094: if (tmp[2].indexOf('x') > 0) {
095: String[] dim = tmp[2].split("x");
096: return new int[] { Integer.parseInt(dim[0]),
097: Integer.parseInt(dim[1]) };
098: }
099: }
100: if (start > 0) {
101: String[] data = line.substring(start).split(" ");
102: String[] dim = data[1].split("x");
103: return new int[] { Integer.parseInt(dim[0]),
104: Integer.parseInt(dim[1]) };
105: }
106: } catch (Exception e) {
107: return new int[] { 0, 0 };
108: }
109: return new int[] { 0, 0 };
110: }
111:
112: static Pattern pNumeric = Pattern.compile("^\\s*\\d+\\: .*");
113: static Pattern pColormap = Pattern.compile("^\\s*Colormap\\: \\d+");
114:
115: // private final static Pattern pSkip = Pattern.compile("^\\s*\\d+\\:.*|^0x.*|^\\s*unknown.*|^\\s*Custom Field.*");
116:
117: /**
118: * Parse an identify stdOut result (from in) and convert it to an XML content
119: *
120: * @param in identify response
121: * @return XML content
122: * @throws XMLStreamException on errors
123: * @throws IOException on errors
124: */
125: public static String parse(InputStream in)
126: throws XMLStreamException, IOException {
127: StringWriter sw = new StringWriter(2000);
128: BufferedReader br = new BufferedReader(
129: new InputStreamReader(in));
130: XMLStreamWriter writer = XMLOutputFactory.newInstance()
131: .createXMLStreamWriter(sw);
132: writer.writeStartDocument();
133:
134: int lastLevel = 0, level, lastNonValueLevel = 1;
135: boolean valueEntry;
136: String curr = null;
137: String[] entry;
138: try {
139: while ((curr = br.readLine()) != null) {
140: level = getLevel(curr);
141: if (level == 0 && curr.startsWith("Image:")) {
142: writer.writeStartElement("Image");
143: entry = curr.split(": ");
144: if (entry.length >= 2)
145: writer.writeAttribute("source", entry[1]);
146: lastLevel = level;
147: continue;
148: }
149: if (!(valueEntry = pNumeric.matcher(curr).matches())) {
150: while (level < lastLevel--)
151: writer.writeEndElement();
152: lastNonValueLevel = level;
153: } else
154: level = lastNonValueLevel + 1;
155: if (curr.endsWith(":")) {
156: writer.writeStartElement(curr.substring(0,
157: curr.lastIndexOf(':')).trim().replaceAll(
158: "[ :]", "-"));
159: lastLevel = level + 1;
160: continue;
161: } else if (pColormap.matcher(curr).matches()) {
162: writer.writeStartElement(curr.substring(0,
163: curr.lastIndexOf(':')).trim().replaceAll(
164: "[ :]", "-"));
165: writer.writeAttribute("colors", curr.split(": ")[1]
166: .trim());
167: lastLevel = level + 1;
168: continue;
169: }
170: entry = curr.split(": ");
171: if (entry.length == 2) {
172: if (!valueEntry) {
173: writer.writeStartElement(entry[0].trim()
174: .replaceAll("[ :]", "-"));
175: writer.writeCharacters(entry[1]);
176: writer.writeEndElement();
177: } else {
178: writer.writeEmptyElement("value");
179: writer.writeAttribute("key", entry[0].trim()
180: .replaceAll("[ :]", "-"));
181: writer.writeAttribute("data", entry[1]);
182: // writer.writeEndElement();
183: }
184: } else {
185: // System.out.println("unknown line: "+curr);
186: }
187: lastLevel = level;
188: }
189: } catch (Exception e) {
190: System.err.println("Error at [" + curr + "]:"
191: + e.getMessage());
192: e.printStackTrace();
193: }
194:
195: writer.writeEndDocument();
196: writer.flush();
197: writer.close();
198: return sw.getBuffer().toString();
199: }
200:
201: /**
202: * Process a file with ImageMagick's identify -verify and return the result as XML
203: *
204: * @param file the file to process
205: * @return meta data as XML
206: * @throws XMLStreamException on errors
207: * @throws IOException on errors
208: * @throws FxApplicationException on errors
209: */
210: public static String getMetaData(File file)
211: throws XMLStreamException, IOException,
212: FxApplicationException {
213: FxSharedUtils.ProcessResult res = FxSharedUtils.executeCommand(
214: IMParser.IDENTIFY_BINARY, "-verbose", file
215: .getAbsolutePath());
216: if (res.getExitCode() != 0)
217: throw new FxApplicationException(
218: "ex.executeCommand.failed",
219: IMParser.IDENTIFY_BINARY, res.getStdErr());
220: return parse(new ByteArrayInputStream(FxSharedUtils
221: .getBytes(res.getStdOut())));
222: }
223:
224: /**
225: * Scale an image and return the dimensions (width and height) as int array
226: *
227: * @param original original file
228: * @param scaled scaled file
229: * @param extension extension
230: * @param width desired width
231: * @param height desired height
232: * @return actual width ([0]) and height ([1]) of scaled image
233: * @throws FxApplicationException on errors
234: */
235: public static int[] scale(File original, File scaled,
236: String extension, int width, int height)
237: throws FxApplicationException {
238: FxSharedUtils.ProcessResult res = FxSharedUtils.executeCommand(
239: IMParser.CONVERT_BINARY, "-scale",
240: width + "x" + height, original.getAbsolutePath(),
241: scaled.getAbsolutePath());
242: if (res.getExitCode() != 0)
243: throw new FxApplicationException(
244: "ex.executeCommand.failed",
245: IMParser.CONVERT_BINARY, res.getStdErr());
246: res = FxSharedUtils.executeCommand(IMParser.IDENTIFY_BINARY,
247: "-ping", FxSharedUtils.escapePath(scaled
248: .getAbsolutePath()));
249: if (res.getExitCode() != 0)
250: throw new FxApplicationException(
251: "ex.executeCommand.failed",
252: IMParser.IDENTIFY_BINARY, res.getStdErr());
253: return getPingDimensions(extension, res.getStdOut());
254: }
255: }
|