/* * #%L * BSD implementations of Bio-Formats readers and writers * %% * Copyright (C) 2005 - 2015 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package loci.formats.out; import java.io.IOException; import java.util.Vector; import loci.common.Constants; import loci.common.services.DependencyException; import loci.common.services.ServiceException; import loci.common.services.ServiceFactory; import loci.common.xml.BaseHandler; 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.ome.OMEXMLMetadata; 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. */ 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]; } // -- FormatWriter API methods -- /* @see loci.formats.FormatWriter#setId(String) */ @Override public void setId(String id) throws FormatException, IOException { if (id.equals(currentId)) { return; } super.setId(id); MetadataRetrieve retrieve = getMetadataRetrieve(); String xml; try { ServiceFactory factory = new ServiceFactory(); service = factory.getInstance(OMEXMLService.class); xml = service.getOMEXML(retrieve); OMEXMLMetadata noBin = service.createOMEXMLMetadata(xml); service.removeBinData(noBin); xml = service.getOMEXML(noBin); } 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() */ @Override 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) */ @Override 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, Constants.ENCODING)); plane.append("</BinData>"); out.writeBytes(plane.toString()); } } /* @see loci.formats.IFormatWriter#canDoStacks() */ @Override public boolean canDoStacks() { return true; } /* @see loci.formats.IFormatWriter#getPixelTypes(String) */ @Override 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 BaseHandler { @Override public void characters(char[] ch, int start, int length) { currentFragment += new String(ch, start, length); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) { StringBuffer toAppend = new StringBuffer("\n<"); toAppend.append(XMLTools.escapeXML(qName)); for (int i=0; i<attributes.getLength(); i++) { toAppend.append(" "); toAppend.append(XMLTools.escapeXML(attributes.getQName(i))); toAppend.append("=\""); toAppend.append(XMLTools.escapeXML(attributes.getValue(i))); toAppend.append("\""); } toAppend.append(">"); currentFragment += toAppend.toString(); } @Override public void endElement(String uri, String localName, String qName) { if (qName.equals("Pixels")) { xmlFragments.add(currentFragment); currentFragment = ""; } currentFragment += "</" + qName + ">"; } } }