001/* ***** BEGIN LICENSE BLOCK ***** 002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 003 * 004 * The contents of this file are subject to the Mozilla Public License Version 005 * 1.1 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * http://www.mozilla.org/MPL/ 008 * 009 * Software distributed under the License is distributed on an "AS IS" basis, 010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 011 * for the specific language governing rights and limitations under the 012 * License. 013 * 014 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in 015 * Java(TM), hosted at https://github.com/gunterze/dcm4che. 016 * 017 * The Initial Developer of the Original Code is 018 * Agfa Healthcare. 019 * Portions created by the Initial Developer are Copyright (C) 2011 020 * the Initial Developer. All Rights Reserved. 021 * 022 * Contributor(s): 023 * See @authors listed below 024 * 025 * Alternatively, the contents of this file may be used under the terms of 026 * either the GNU General Public License Version 2 or later (the "GPL"), or 027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 028 * in which case the provisions of the GPL or the LGPL are applicable instead 029 * of those above. If you wish to allow use of your version of this file only 030 * under the terms of either the GPL or the LGPL, and not to allow others to 031 * use your version of this file under the terms of the MPL, indicate your 032 * decision by deleting the provisions above and replace them with the notice 033 * and other provisions required by the GPL or the LGPL. If you do not delete 034 * the provisions above, a recipient may use your version of this file under 035 * the terms of any one of the MPL, the GPL or the LGPL. 036 * 037 * ***** END LICENSE BLOCK ***** */ 038 039package org.dcm4che3.tool.dcm2xml; 040 041import java.io.BufferedOutputStream; 042import java.io.File; 043import java.io.FileOutputStream; 044import java.io.IOException; 045import java.io.OutputStream; 046import java.net.MalformedURLException; 047import java.net.URL; 048import java.util.List; 049import java.util.ResourceBundle; 050 051import javax.xml.parsers.SAXParser; 052import javax.xml.parsers.SAXParserFactory; 053import javax.xml.transform.OutputKeys; 054import javax.xml.transform.Transformer; 055import javax.xml.transform.TransformerConfigurationException; 056import javax.xml.transform.TransformerFactory; 057import javax.xml.transform.sax.SAXTransformerFactory; 058import javax.xml.transform.sax.TransformerHandler; 059import javax.xml.transform.stream.StreamResult; 060import javax.xml.transform.stream.StreamSource; 061 062import org.apache.commons.cli.CommandLine; 063import org.apache.commons.cli.OptionBuilder; 064import org.apache.commons.cli.OptionGroup; 065import org.apache.commons.cli.Options; 066import org.apache.commons.cli.ParseException; 067import org.dcm4che3.data.Attributes; 068import org.dcm4che3.io.BulkDataDescriptor; 069import org.dcm4che3.io.ContentHandlerAdapter; 070import org.dcm4che3.io.DicomInputStream; 071import org.dcm4che3.io.DicomInputStream.IncludeBulkData; 072import org.dcm4che3.io.SAXWriter; 073import org.dcm4che3.tool.common.CLIUtils; 074 075/** 076 * Tool for converting DICOM to XML presentation and optionally apply XSLT 077 * stylesheet on it. 078 * 079 * @author Gunter Zeilinger <gunterze@gmail.com> 080 */ 081public class Dcm2Xml { 082 083 private static final String XML_1_0 = "1.0"; 084 private static final String XML_1_1 = "1.1"; 085 086 private static ResourceBundle rb = 087 ResourceBundle.getBundle("org.dcm4che3.tool.dcm2xml.messages"); 088 089 private String xsltURL; 090 private boolean indent = false; 091 private boolean includeKeyword = true; 092 private boolean includeNamespaceDeclaration = false; 093 private IncludeBulkData includeBulkData = IncludeBulkData.URI; 094 private boolean catBlkFiles = false; 095 private String blkFilePrefix = "blk"; 096 private String blkFileSuffix; 097 private File blkDirectory; 098 private Attributes blkAttrs; 099 private String xmlVersion = XML_1_0; 100 101 public final void setXSLTURL(String xsltURL) { 102 this.xsltURL = xsltURL; 103 } 104 105 public final void setIndent(boolean indent) { 106 this.indent = indent; 107 } 108 109 public final void setIncludeKeyword(boolean includeKeyword) { 110 this.includeKeyword = includeKeyword; 111 } 112 113 public final void setIncludeNamespaceDeclaration(boolean includeNamespaceDeclaration) { 114 this.includeNamespaceDeclaration = includeNamespaceDeclaration; 115 } 116 117 public final void setIncludeBulkData(IncludeBulkData includeBulkData) { 118 this.includeBulkData = includeBulkData; 119 } 120 121 public final void setConcatenateBulkDataFiles(boolean catBlkFiles) { 122 this.catBlkFiles = catBlkFiles; 123 } 124 125 public final void setBulkDataFilePrefix(String blkFilePrefix) { 126 this.blkFilePrefix = blkFilePrefix; 127 } 128 129 public final void setBulkDataFileSuffix(String blkFileSuffix) { 130 this.blkFileSuffix = blkFileSuffix; 131 } 132 133 public final void setBulkDataDirectory(File blkDirectory) { 134 this.blkDirectory = blkDirectory; 135 } 136 137 public final void setBulkDataAttributes(Attributes blkAttrs) { 138 this.blkAttrs = blkAttrs; 139 } 140 141 public final void setXMLVersion(String xmlVersion) { 142 this.xmlVersion = xmlVersion; 143 } 144 145 @SuppressWarnings("static-access") 146 private static CommandLine parseComandLine(String[] args) 147 throws ParseException { 148 Options opts = new Options(); 149 CLIUtils.addCommonOptions(opts); 150 opts.addOption(OptionBuilder 151 .withLongOpt("xsl") 152 .hasArg() 153 .withArgName("xsl-file") 154 .withDescription(rb.getString("xsl")) 155 .create("x")); 156 opts.addOption("I", "indent", false, rb.getString("indent")); 157 opts.addOption("K", "no-keyword", false, rb.getString("no-keyword")); 158 opts.addOption(null, "xmlns", false, rb.getString("xmlns")); 159 opts.addOption(null, "xml11", false, rb.getString("xml11")); 160 addBulkdataOptions(opts); 161 162 return CLIUtils.parseComandLine(args, opts, rb, Dcm2Xml.class); 163 } 164 165 @SuppressWarnings("static-access") 166 private static void addBulkdataOptions(Options opts) { 167 OptionGroup group = new OptionGroup(); 168 group.addOption(OptionBuilder 169 .withLongOpt("no-bulkdata") 170 .withDescription(rb.getString("no-bulkdata")) 171 .create("B")); 172 group.addOption(OptionBuilder 173 .withLongOpt("with-bulkdata") 174 .withDescription(rb.getString("with-bulkdata")) 175 .create("b")); 176 opts.addOptionGroup(group); 177 opts.addOption(OptionBuilder 178 .withLongOpt("blk-file-dir") 179 .hasArg() 180 .withArgName("directory") 181 .withDescription(rb.getString("blk-file-dir")) 182 .create("d")); 183 opts.addOption(OptionBuilder 184 .withLongOpt("blk-file-prefix") 185 .hasArg() 186 .withArgName("prefix") 187 .withDescription(rb.getString("blk-file-prefix")) 188 .create()); 189 opts.addOption(OptionBuilder 190 .withLongOpt("blk-file-suffix") 191 .hasArg() 192 .withArgName("suffix") 193 .withDescription(rb.getString("blk-file-dir")) 194 .create()); 195 opts.addOption("c", "cat-blk-files", false, 196 rb.getString("cat-blk-files")); 197 opts.addOption(OptionBuilder 198 .withLongOpt("blk-spec") 199 .hasArg() 200 .withArgName("xml-file") 201 .withDescription(rb.getString("blk-spec")) 202 .create("X")); 203 } 204 205 @SuppressWarnings("unchecked") 206 public static void main(String[] args) { 207 try { 208 CommandLine cl = parseComandLine(args); 209 Dcm2Xml main = new Dcm2Xml(); 210 if (cl.hasOption("x")) 211 main.setXSLTURL(toURL(cl.getOptionValue("x"))); 212 main.setIndent(cl.hasOption("I")); 213 main.setIncludeKeyword(!cl.hasOption("K")); 214 main.setIncludeNamespaceDeclaration(cl.hasOption("xmlns")); if (cl.hasOption("xml11")) 215 main.setXMLVersion(XML_1_1); 216 configureBulkdata(main, cl); 217 String fname = fname(cl.getArgList()); 218 if (fname.equals("-")) { 219 main.convert(new DicomInputStream(System.in), System.out); 220 } else { 221 main.convert(new File(fname), System.out); 222 } 223 } catch (ParseException e) { 224 System.err.println("dcm2xml: " + e.getMessage()); 225 System.err.println(rb.getString("try")); 226 System.exit(2); 227 } catch (Exception e) { 228 System.err.println("dcm2xml: " + e.getMessage()); 229 e.printStackTrace(); 230 System.exit(2); 231 } 232 } 233 234 private static String toURL(String fileOrURL) { 235 try { 236 new URL(fileOrURL); 237 return fileOrURL; 238 } catch (MalformedURLException e) { 239 return new File(fileOrURL).toURI().toString(); 240 } 241 } 242 243 private static void configureBulkdata(Dcm2Xml dcm2xml, CommandLine cl) 244 throws Exception { 245 if (cl.hasOption("b")) { 246 dcm2xml.setIncludeBulkData(IncludeBulkData.YES); 247 } 248 if (cl.hasOption("B")) { 249 dcm2xml.setIncludeBulkData(IncludeBulkData.NO); 250 } 251 if (cl.hasOption("blk-file-prefix")) { 252 dcm2xml.setBulkDataFilePrefix( 253 cl.getOptionValue("blk-file-prefix")); 254 } 255 if (cl.hasOption("blk-file-suffix")) { 256 dcm2xml.setBulkDataFileSuffix( 257 cl.getOptionValue("blk-file-suffix")); 258 } 259 if (cl.hasOption("d")) { 260 File tempDir = new File(cl.getOptionValue("d")); 261 dcm2xml.setBulkDataDirectory(tempDir); 262 } 263 dcm2xml.setConcatenateBulkDataFiles(cl.hasOption("c")); 264 if (cl.hasOption("X")) { 265 dcm2xml.setBulkDataAttributes( 266 parseXML(cl.getOptionValue("X"))); 267 } 268 } 269 270 private static Attributes parseXML(String fname) throws Exception { 271 Attributes attrs = new Attributes(); 272 ContentHandlerAdapter ch = new ContentHandlerAdapter(attrs); 273 SAXParserFactory f = SAXParserFactory.newInstance(); 274 SAXParser p = f.newSAXParser(); 275 p.parse(new File(fname), ch); 276 return attrs; 277 } 278 279 private static String fname(List<String> argList) throws ParseException { 280 int numArgs = argList.size(); 281 if (numArgs == 0) 282 throw new ParseException(rb.getString("missing")); 283 if (numArgs > 1) 284 throw new ParseException(rb.getString("too-many")); 285 return argList.get(0); 286 } 287 288 public void convert(DicomInputStream dis, OutputStream out) throws IOException, 289 TransformerConfigurationException { 290 dis.setIncludeBulkData(includeBulkData); 291 if (blkAttrs != null) 292 dis.setBulkDataDescriptor(BulkDataDescriptor.valueOf(blkAttrs)); 293 dis.setBulkDataDirectory(blkDirectory); 294 dis.setBulkDataFilePrefix(blkFilePrefix); 295 dis.setBulkDataFileSuffix(blkFileSuffix); 296 dis.setConcatenateBulkDataFiles(catBlkFiles); 297 TransformerHandler th = getTransformerHandler(); 298 Transformer t = th.getTransformer(); 299 t.setOutputProperty(OutputKeys.INDENT, indent ? "yes" : "no"); 300 t.setOutputProperty(OutputKeys.VERSION, xmlVersion); 301 th.setResult(new StreamResult(out)); 302 SAXWriter saxWriter = new SAXWriter(th); 303 saxWriter.setIncludeKeyword(includeKeyword); 304 saxWriter.setIncludeNamespaceDeclaration(includeNamespaceDeclaration); 305 dis.setDicomInputHandler(saxWriter); 306 dis.readDataset(-1, -1); 307 } 308 309 public void convert(File dicomFile, OutputStream out) throws IOException, 310 TransformerConfigurationException { 311 DicomInputStream dis = new DicomInputStream(dicomFile); 312 try { 313 convert(dis, out); 314 } finally { 315 dis.close(); 316 } 317 } 318 319 public void convert(File dicomFile, File xmlFile) throws IOException, 320 TransformerConfigurationException { 321 OutputStream out = new BufferedOutputStream(new FileOutputStream(xmlFile)); 322 try { 323 convert(dicomFile, out); 324 } finally { 325 out.close(); 326 } 327 } 328 329 private TransformerHandler getTransformerHandler() 330 throws TransformerConfigurationException, IOException { 331 SAXTransformerFactory tf = (SAXTransformerFactory) 332 TransformerFactory.newInstance(); 333 if (xsltURL == null) 334 return tf.newTransformerHandler(); 335 336 TransformerHandler th = tf.newTransformerHandler( 337 new StreamSource(xsltURL)); 338 return th; 339 } 340}