// // OMEXMLWriter.java // /* OME Bio-Formats package for reading and converting biological file formats. Copyright (C) 2005-@year@ UW-Madison LOCI and Glencoe Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package loci.formats.out; import java.io.IOException; import java.util.Vector; import loci.common.services.DependencyException; import loci.common.services.ServiceException; import loci.common.services.ServiceFactory; import loci.common.xml.XMLTools; import loci.formats.FormatException; import loci.formats.FormatTools; import loci.formats.FormatWriter; import loci.formats.ImageTools; import loci.formats.MissingLibraryException; import loci.formats.codec.Base64Codec; import loci.formats.codec.CodecOptions; import loci.formats.codec.CompressionType; import loci.formats.codec.JPEG2000Codec; import loci.formats.codec.JPEGCodec; import loci.formats.codec.ZlibCodec; import loci.formats.meta.MetadataRetrieve; import loci.formats.services.OMEXMLService; import loci.formats.services.OMEXMLServiceImpl; import org.xml.sax.Attributes; import org.xml.sax.helpers.DefaultHandler; /** * OMEXMLWriter is the file format writer for OME-XML files. * * <dl><dt><b>Source code:</b></dt> * <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/bio-formats/src/loci/formats/out/OMEXMLWriter.java">Trac</a>, * <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/out/OMEXMLWriter.java;hb=HEAD">Gitweb</a></dd></dl> */ public class OMEXMLWriter extends FormatWriter { // -- Fields -- private Vector<String> xmlFragments; private String currentFragment; private OMEXMLService service; // -- Constructor -- public OMEXMLWriter() { super("OME-XML", "ome"); compressionTypes = new String[] {CompressionType.UNCOMPRESSED.getCompression(), CompressionType.ZLIB.getCompression()}; compression = compressionTypes[0]; } // -- IFormatHandler API methods -- /* @see loci.formats.IFormatHandler#setId(String) */ public void setId(String id) throws FormatException, IOException { super.setId(id); MetadataRetrieve retrieve = getMetadataRetrieve(); String xml; try { ServiceFactory factory = new ServiceFactory(); service = factory.getInstance(OMEXMLService.class); xml = service.getOMEXML(retrieve); } catch (DependencyException de) { throw new MissingLibraryException(OMEXMLServiceImpl.NO_OME_XML_MSG, de); } catch (ServiceException se) { throw new FormatException(se); } xmlFragments = new Vector<String>(); currentFragment = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; XMLTools.parseXML(xml, new OMEHandler()); xmlFragments.add(currentFragment); } /* @see loci.formats.IFormatHandler#close() */ public void close() throws IOException { if (out != null) { out.writeBytes(xmlFragments.get(xmlFragments.size() - 1)); } super.close(); xmlFragments = null; service = null; } // -- IFormatWriter API methods -- /** * @see loci.formats.IFormatWriter#saveBytes(int, byte[], int, int, int, int) */ public void saveBytes(int no, byte[] buf, int x, int y, int w, int h) throws FormatException, IOException { checkParams(no, buf, x, y, w, h); if (!isFullPlane(x, y, w, h)) { throw new FormatException( "OMEXMLWriter does not yet support saving image tiles."); } MetadataRetrieve retrieve = getMetadataRetrieve(); if (no == 0) { out.writeBytes(xmlFragments.get(series)); } String type = retrieve.getPixelsType(series).toString(); int pixelType = FormatTools.pixelTypeFromString(type); int bytes = FormatTools.getBytesPerPixel(pixelType); int nChannels = getSamplesPerPixel(); int sizeX = retrieve.getPixelsSizeX(series).getValue().intValue(); int sizeY = retrieve.getPixelsSizeY(series).getValue().intValue(); int planeSize = sizeX * sizeY * bytes; boolean bigEndian = retrieve.getPixelsBinDataBigEndian(series, 0); String namespace = "xmlns=\"http://www.openmicroscopy.org/Schemas/BinaryFile/" + service.getLatestVersion() + "\""; for (int i=0; i<nChannels; i++) { byte[] b = ImageTools.splitChannels(buf, i, nChannels, bytes, false, interleaved); byte[] encodedPix = compress(b); StringBuffer plane = new StringBuffer("\n<BinData "); plane.append(namespace); plane.append(" Length=\""); plane.append(planeSize); plane.append("\""); plane.append(" BigEndian=\""); plane.append(bigEndian); plane.append("\""); if (compression != null && !compression.equals("Uncompressed")) { plane.append(" Compression=\""); plane.append(compression); plane.append("\""); } plane.append(">"); plane.append(new String(encodedPix)); plane.append("</BinData>"); out.writeBytes(plane.toString()); } } /* @see loci.formats.IFormatWriter#canDoStacks() */ public boolean canDoStacks() { return true; } /* @see loci.formats.IFormatWriter#getPixelTypes(String) */ public int[] getPixelTypes(String codec) { if (codec != null && (codec.equals("J2K") || codec.equals("JPEG"))) { return new int[] {FormatTools.INT8, FormatTools.UINT8}; } return super.getPixelTypes(codec); } // -- Helper methods -- /** * Compress the given byte array using the current codec. * The compressed data is then base64-encoded. */ private byte[] compress(byte[] b) throws FormatException, IOException { MetadataRetrieve r = getMetadataRetrieve(); String type = r.getPixelsType(series).toString(); int pixelType = FormatTools.pixelTypeFromString(type); int bytes = FormatTools.getBytesPerPixel(pixelType); CodecOptions options = new CodecOptions(); options.width = r.getPixelsSizeX(series).getValue().intValue(); options.height = r.getPixelsSizeY(series).getValue().intValue(); options.channels = 1; options.interleaved = false; options.signed = FormatTools.isSigned(pixelType); options.littleEndian = !r.getPixelsBinDataBigEndian(series, 0).booleanValue(); options.bitsPerSample = bytes * 8; if (compression.equals("J2K")) { b = new JPEG2000Codec().compress(b, options); } else if (compression.equals("JPEG")) { b = new JPEGCodec().compress(b, options); } else if (compression.equals("zlib")) { b = new ZlibCodec().compress(b, options); } return new Base64Codec().compress(b, options); } // -- Helper class -- class OMEHandler extends DefaultHandler { public void characters(char[] ch, int start, int length) { currentFragment += new String(ch, start, length); } public void startElement(String uri, String localName, String qName, Attributes attributes) { StringBuffer toAppend = new StringBuffer("\n<"); toAppend.append(qName); for (int i=0; i<attributes.getLength(); i++) { toAppend.append(" "); toAppend.append(attributes.getQName(i)); toAppend.append("=\""); toAppend.append(attributes.getValue(i)); toAppend.append("\""); } toAppend.append(">"); currentFragment += toAppend.toString(); } public void endElement(String uri, String localName, String qName) { currentFragment += "</" + qName + ">"; if (qName.equals("Channel")) { xmlFragments.add(currentFragment); currentFragment = ""; } } } }