Java tutorial
/*************************************************************** * This file is part of the [fleXive](R) framework. * * Copyright (c) 1999-2014 * UCS - unique computing solutions gmbh (http://www.ucs.at) * All rights reserved * * The [fleXive](R) project is free software; you can redistribute * it and/or modify it under the terms of the GNU Lesser General Public * License version 2.1 or higher as published by the Free Software Foundation. * * The GNU Lesser General Public License can be found at * http://www.gnu.org/licenses/lgpl.html. * A copy is found in the textfile LGPL.txt and important notices to the * license from the author are found in LICENSE.txt distributed with * these libraries. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For further information about UCS - unique computing solutions gmbh, * please see the company website: http://www.ucs.at * * For further information about [fleXive](R), please see the * project website: http://www.flexive.org * * * This copyright notice MUST APPEAR in all copies of the file! ***************************************************************/ package com.flexive.core; import com.flexive.shared.FxSharedUtils; import com.flexive.shared.exceptions.FxApplicationException; import org.apache.commons.lang.StringUtils; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import java.io.*; import java.util.regex.Pattern; /** * ImageMagick identify parser * * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at) */ public class IMParser { // Name of the identify executeable public final static String IDENTIFY_BINARY = "identify"; // Name of the convert executeable public final static String CONVERT_BINARY = "convert"; /** * Get the identation depth of the current line (2 characters = 1 level) * * @param data line to examine * @return identation depth */ private static int getLevel(String data) { if (data == null || data.length() == 0) return 0; int ident = 0; for (int i = 0; i < data.length(); i++) { if (data.charAt(i) != ' ') return ident / 2; ident++; } if (ident == 0) return ident; return ident / 2; } /** * Parse a ping response from ImageMagick for image dimensions * * @param extension extension of the file * @param line the response from ImageMagick's ping command * @return array containing dimensions or {0,0} if an error occured */ public static int[] getPingDimensions(String extension, String line) { try { int start = 0; if (extension.equals(".JPG")) start = line.indexOf(" JPEG ") + 1; if (start <= 0 && extension.equals(".PNG")) start = line.indexOf(" PNG ") + 1; if (start <= 0 && extension.equals(".GIF")) start = line.indexOf(" GIF ") + 1; if (start <= 0) { String[] tmp = line.split(" "); if (tmp[2].indexOf('x') > 0) { String[] dim = tmp[2].split("x"); return new int[] { Integer.parseInt(dim[0]), Integer.parseInt(dim[1]) }; } } if (start > 0) { String[] data = line.substring(start).split(" "); String[] dim = data[1].split("x"); return new int[] { Integer.parseInt(dim[0]), Integer.parseInt(dim[1]) }; } } catch (Exception e) { return new int[] { 0, 0 }; } return new int[] { 0, 0 }; } static Pattern pNumeric = Pattern.compile("^\\s*\\d+\\: .*"); static Pattern pColormap = Pattern.compile("^\\s*Colormap\\: \\d+"); // private final static Pattern pSkip = Pattern.compile("^\\s*\\d+\\:.*|^0x.*|^\\s*unknown.*|^\\s*Custom Field.*"); /** * Remove invalid characters from an xml element * * @param element element to clean * @return cleaned element * @since 3.1.1 */ private static String cleanElement(String element) { if (StringUtils.isBlank(element)) return "empty-element"; return element.trim().replaceAll("[ :]", "-").replaceAll("\\[.*\\]", ""); } /** * Parse an identify stdOut result (from in) and convert it to an XML content * * @param in identify response * @return XML content * @throws XMLStreamException on errors * @throws IOException on errors */ public static String parse(InputStream in) throws XMLStreamException, IOException { StringWriter sw = new StringWriter(2000); BufferedReader br = new BufferedReader(new InputStreamReader(in)); XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(sw); writer.writeStartDocument(); int lastLevel = 0, level, lastNonValueLevel = 1; boolean valueEntry; String curr = null; String[] entry; try { while ((curr = br.readLine()) != null) { if (curr.indexOf(':') == -1 || curr.trim().length() == 0) { System.out.println("skipping: [" + curr + "]"); continue; //ignore lines without ':' } level = getLevel(curr); curr = curr.trim(); if (level == 0 && curr.startsWith("Image:")) { writer.writeStartElement("Image"); entry = curr.split(": "); if (entry.length >= 2) writer.writeAttribute("source", entry[1]); lastLevel = level; continue; } if (!(valueEntry = pNumeric.matcher(curr).matches())) { while (level < lastLevel--) writer.writeEndElement(); lastNonValueLevel = level; } else level = lastNonValueLevel + 1; if (curr.endsWith(":")) { writer.writeStartElement(cleanElement(curr.substring(0, curr.lastIndexOf(':')))); lastLevel = level + 1; continue; } else if (pColormap.matcher(curr).matches()) { writer.writeStartElement(cleanElement(curr.substring(0, curr.lastIndexOf(':')))); writer.writeAttribute("colors", curr.split(": ")[1].trim()); lastLevel = level + 1; continue; } entry = curr.split(": "); if (entry.length == 2) { if (!valueEntry) { writer.writeStartElement(cleanElement(entry[0])); writer.writeCharacters(entry[1]); writer.writeEndElement(); } else { writer.writeEmptyElement("value"); writer.writeAttribute("key", cleanElement(entry[0])); writer.writeAttribute("data", entry[1]); // writer.writeEndElement(); } } else { // System.out.println("unknown line: "+curr); } lastLevel = level; } } catch (Exception e) { System.err.println("Error at [" + curr + "]:" + e.getMessage()); e.printStackTrace(); } writer.writeEndDocument(); writer.flush(); writer.close(); return sw.getBuffer().toString(); } /** * Process a file with ImageMagick's identify -verify and return the result as XML * * @param file the file to process * @return meta data as XML * @throws XMLStreamException on errors * @throws IOException on errors * @throws FxApplicationException on errors */ public static String getMetaData(File file) throws XMLStreamException, IOException, FxApplicationException { FxSharedUtils.ProcessResult res = FxSharedUtils.executeCommand(IMParser.IDENTIFY_BINARY, "-verbose", file.getAbsolutePath()); if (res.getExitCode() != 0) throw new FxApplicationException("ex.executeCommand.failed", IMParser.IDENTIFY_BINARY, res.getStdErr()); return parse(new ByteArrayInputStream(FxSharedUtils.getBytes(res.getStdOut()))); } /** * Scale an image and return the dimensions (width and height) as int array * * @param original original file * @param scaled scaled file * @param extension extension * @param width desired width * @param height desired height * @return actual width ([0]) and height ([1]) of scaled image * @throws FxApplicationException on errors */ public static int[] scale(File original, File scaled, String extension, int width, int height) throws FxApplicationException { FxSharedUtils.ProcessResult res = FxSharedUtils.executeCommand(IMParser.CONVERT_BINARY, "-scale", width + "x" + height, original.getAbsolutePath(), scaled.getAbsolutePath()); if (res.getExitCode() != 0) throw new FxApplicationException("ex.executeCommand.failed", IMParser.CONVERT_BINARY, res.getStdErr()); res = FxSharedUtils.executeCommand(IMParser.IDENTIFY_BINARY, "-ping", FxSharedUtils.escapePath(scaled.getAbsolutePath())); if (res.getExitCode() != 0) throw new FxApplicationException("ex.executeCommand.failed", IMParser.IDENTIFY_BINARY, res.getStdErr()); return getPingDimensions(extension, res.getStdOut()); } }