--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.ooxml.dev;
+
+import java.io.*;
+import java.util.ArrayList;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.openxml4j.opc.PackageAccess;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
+
+/**
+ * Prints out the contents of a OOXML container.
+ * Useful for seeing what parts are defined, and how
+ * they're all related to each other.
+ */
+public class OOXMLLister implements Closeable {
+ private final OPCPackage container;
+ private final PrintStream disp;
+
+ public OOXMLLister(OPCPackage container) {
+ this(container, System.out);
+ }
+ public OOXMLLister(OPCPackage container, PrintStream disp) {
+ this.container = container;
+ this.disp = disp;
+ }
+
+ /**
+ * Figures out how big a given PackagePart is.
+ *
+ * @param part the PackagePart
+ * @return the size of the PackagePart
+ *
+ * @throws IOException if the part can't be read
+ */
+ public static long getSize(PackagePart part) throws IOException {
+ try (InputStream in = part.getInputStream()) {
+ byte[] b = new byte[8192];
+ long size = 0;
+ int read = 0;
+
+ while (read > -1) {
+ read = in.read(b);
+ if (read > 0) {
+ size += read;
+ }
+ }
+
+ return size;
+ }
+ }
+
+ /**
+ * Displays information on all the different
+ * parts of the OOXML file container.
+ * @throws InvalidFormatException if the package relations are invalid
+ * @throws IOException if the package can't be read
+ */
+ public void displayParts() throws InvalidFormatException, IOException {
+ ArrayList<PackagePart> parts = container.getParts();
+ for (PackagePart part : parts) {
+ disp.println(part.getPartName());
+ disp.println("\t" + part.getContentType());
+
+ if(! part.getPartName().toString().equals("/docProps/core.xml")) {
+ disp.println("\t" + getSize(part) + " bytes");
+ }
+
+ if(! part.isRelationshipPart()) {
+ disp.println("\t" + part.getRelationships().size() + " relations");
+ for(PackageRelationship rel : part.getRelationships()) {
+ displayRelation(rel, "\t ");
+ }
+ }
+ }
+ }
+ /**
+ * Displays information on all the different
+ * relationships between different parts
+ * of the OOXML file container.
+ */
+ public void displayRelations() {
+ PackageRelationshipCollection rels =
+ container.getRelationships();
+ for (PackageRelationship rel : rels) {
+ displayRelation(rel, "");
+ }
+ }
+
+ private void displayRelation(PackageRelationship rel, String indent) {
+ disp.println(indent+"Relationship:");
+ disp.println(indent+"\tFrom: "+ rel.getSourceURI());
+ disp.println(indent+"\tTo: " + rel.getTargetURI());
+ disp.println(indent+"\tID: " + rel.getId());
+ disp.println(indent+"\tMode: " + rel.getTargetMode());
+ disp.println(indent+"\tType: " + rel.getRelationshipType());
+ }
+
+ @Override
+ public void close() throws IOException {
+ container.close();
+ }
+
+ public static void main(String[] args) throws IOException, InvalidFormatException {
+ if(args.length == 0) {
+ System.err.println("Use:");
+ System.err.println("\tjava OOXMLLister <filename>");
+ System.exit(1);
+ }
+
+ File f = new File(args[0]);
+ if(! f.exists()) {
+ System.err.println("Error, file not found!");
+ System.err.println("\t" + f);
+ System.exit(2);
+ }
+
+ try (OOXMLLister lister = new OOXMLLister(
+ OPCPackage.open(f.toString(), PackageAccess.READ)
+ )) {
+ lister.disp.println(f + "\n");
+ lister.displayParts();
+ lister.disp.println();
+ lister.displayRelations();
+ }
+ }
+}
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.ooxml.dev;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipOutputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.poi.openxml4j.opc.internal.ZipHelper;
+import org.apache.poi.openxml4j.util.ZipSecureFile;
+import org.apache.poi.util.IOUtils;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+
+/**
+ * Reads a zipped OOXML file and produces a copy with the included
+ * pretty-printed XML files.
+ *
+ * This is useful for comparing OOXML files produced by different tools as the often
+ * use different formatting of the XML.
+ */
+public class OOXMLPrettyPrint {
+ private final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ private final DocumentBuilder documentBuilder;
+
+ public OOXMLPrettyPrint() throws ParserConfigurationException {
+ // allow files with much lower inflation rate here as there is no risk of Zip Bomb attacks in this developer tool
+ ZipSecureFile.setMinInflateRatio(0.00001);
+
+ documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ }
+
+ public static void main(String[] args) throws Exception {
+ if(args.length <= 1 || args.length % 2 != 0) {
+ System.err.println("Use:");
+ System.err.println("\tjava OOXMLPrettyPrint [<filename> <outfilename>] ...");
+ System.exit(1);
+ }
+
+ for(int i = 0;i < args.length;i+=2) {
+ File f = new File(args[i]);
+ if(! f.exists()) {
+ System.err.println("Error, file not found!");
+ System.err.println("\t" + f);
+ System.exit(2);
+ }
+
+ handleFile(f, new File(args[i+1]));
+ }
+ System.out.println("Done.");
+ }
+
+ private static void handleFile(File file, File outFile) throws ZipException,
+ IOException, ParserConfigurationException {
+ System.out.println("Reading zip-file " + file + " and writing pretty-printed XML to " + outFile);
+
+ try (ZipSecureFile zipFile = ZipHelper.openZipFile(file)) {
+ try (ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)))) {
+ new OOXMLPrettyPrint().handle(zipFile, out);
+ }
+ } finally {
+ System.out.println();
+ }
+ }
+
+ private void handle(ZipSecureFile file, ZipOutputStream out) throws IOException {
+ Enumeration<? extends ZipArchiveEntry> entries = file.getEntries();
+ while(entries.hasMoreElements()) {
+ ZipArchiveEntry entry = entries.nextElement();
+
+ String name = entry.getName();
+ out.putNextEntry(new ZipEntry(name));
+ try {
+ if(name.endsWith(".xml") || name.endsWith(".rels")) {
+ Document document = documentBuilder.parse(new InputSource(file.getInputStream(entry)));
+ document.setXmlStandalone(true);
+ pretty(document, out, 2);
+ } else {
+ System.out.println("Not pretty-printing non-XML file " + name);
+ IOUtils.copy(file.getInputStream(entry), out);
+ }
+ } catch (Exception e) {
+ throw new IOException("While handling entry " + name, e);
+ } finally {
+ out.closeEntry();
+ }
+ System.out.print(".");
+ }
+ }
+
+ private static void pretty(Document document, OutputStream outputStream, int indent) throws TransformerException {
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer = transformerFactory.newTransformer();
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ if (indent > 0) {
+ // set properties to indent the resulting XML nicely
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", Integer.toString(indent));
+ }
+ Result result = new StreamResult(outputStream);
+ Source source = new DOMSource(document);
+ transformer.transform(source, result);
+ }
+}