001/*
002 * (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 */
020
021package org.nuxeo.template.odt;
022
023import java.io.ByteArrayInputStream;
024import java.io.File;
025import java.io.FileInputStream;
026import java.io.FileOutputStream;
027import java.io.IOException;
028import java.io.InputStream;
029import java.util.List;
030import java.util.zip.CRC32;
031import java.util.zip.ZipEntry;
032import java.util.zip.ZipOutputStream;
033
034import org.nuxeo.common.utils.FileUtils;
035import org.nuxeo.common.utils.ZipUtils;
036import org.nuxeo.ecm.core.api.Blob;
037
038/**
039 * Helper used to modify a ODT/Zip archive for addition Pictures (and potentially fragments)
040 *
041 * @author Tiry (tdelprat@nuxeo.com)
042 */
043public class OOoArchiveModifier {
044
045    public File updateArchive(File workingDir, File oooFile, List<Blob> blobs) throws IOException {
046        if (blobs == null || blobs.size() == 0) {
047            return oooFile;
048        }
049
050        File unzipDir = new File(workingDir, "unzip-" + oooFile.getName());
051        unzipDir.mkdirs();
052
053        ZipUtils.unzip(oooFile, unzipDir);
054
055        File pictureDirs = new File(unzipDir, "Pictures");
056        if (!pictureDirs.exists()) {
057            pictureDirs.mkdir();
058            pictureDirs = new File(unzipDir, "Pictures");
059        }
060
061        File contentDirs = new File(unzipDir, "Content");
062        if (!contentDirs.exists()) {
063            contentDirs.mkdir();
064            contentDirs = new File(unzipDir, "Content");
065        }
066
067        StringBuffer blobsManifest = new StringBuffer();
068        for (Blob blob : blobs) {
069            if (blob.getMimeType().startsWith("image")) {
070                FileUtils.copyToFile(blob.getStream(), new File(pictureDirs, blob.getFilename()));
071            } else {
072                FileUtils.copyToFile(blob.getStream(), new File(contentDirs, blob.getFilename()));
073            }
074
075            blobsManifest.append("<manifest:file-entry manifest:media-type=\"");
076            blobsManifest.append(blob.getMimeType());
077            if (blob.getMimeType().startsWith("image")) {
078                blobsManifest.append("\" manifest:full-path=\"Pictures/");
079            } else {
080                blobsManifest.append("\" manifest:full-path=\"Content/");
081            }
082            blobsManifest.append(blob.getFilename());
083            blobsManifest.append("\"/>\n");
084        }
085
086        File xmlManifestFile = new File(unzipDir.getPath() + "/META-INF/manifest.xml");
087        String xmlManifest = FileUtils.readFile(xmlManifestFile);
088        int idx = xmlManifest.indexOf("</manifest:manifest>");
089        xmlManifest = xmlManifest.substring(0, idx) + blobsManifest.toString() + xmlManifest.substring(idx);
090        FileUtils.writeFile(xmlManifestFile, xmlManifest.getBytes());
091
092        String path = oooFile.getAbsolutePath();
093
094        oooFile.delete();
095
096        oooFile = new File(path);
097        oooFile.createNewFile();
098
099        // ZipUtils.zip(unzipDir.listFiles(), oooFile);
100        mkOOoZip(unzipDir, oooFile);
101
102        FileUtils.deleteTree(unzipDir);
103
104        return oooFile;
105
106    }
107
108    protected void mkOOoZip(File directory, File outFile) throws IOException {
109
110        ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(outFile));
111
112        File manif = new File(directory, "mimetype");
113        writeOOoEntry(zipOutputStream, manif.getName(), manif, ZipEntry.STORED);
114
115        for (File fileEntry : directory.listFiles()) {
116            if (!fileEntry.getName().equals(manif.getName())) {
117                writeOOoEntry(zipOutputStream, fileEntry.getName(), fileEntry, ZipEntry.DEFLATED);
118            }
119        }
120
121        zipOutputStream.close();
122    }
123
124    protected void writeOOoEntry(ZipOutputStream zipOutputStream, String entryName, File fileEntry, int zipMethod) throws IOException {
125
126        if (fileEntry.isDirectory()) {
127            entryName = entryName + "/";
128            ZipEntry zentry = new ZipEntry(entryName);
129            zipOutputStream.putNextEntry(zentry);
130            zipOutputStream.closeEntry();
131            for (File child : fileEntry.listFiles()) {
132                writeOOoEntry(zipOutputStream, entryName + child.getName(), child, zipMethod);
133            }
134            return;
135        }
136
137        ZipEntry zipEntry = new ZipEntry(entryName);
138        InputStream entryInputStream = new FileInputStream(fileEntry);
139        zipEntry.setMethod(zipMethod);
140        if (zipMethod == ZipEntry.STORED) {
141            byte[] inputBytes = FileUtils.readBytes(entryInputStream);
142            CRC32 crc = new CRC32();
143            crc.update(inputBytes);
144            zipEntry.setCrc(crc.getValue());
145            zipEntry.setSize(inputBytes.length);
146            zipEntry.setCompressedSize(inputBytes.length);
147            zipOutputStream.putNextEntry(zipEntry);
148            FileUtils.copy(new ByteArrayInputStream(inputBytes), zipOutputStream);
149        } else {
150            zipOutputStream.putNextEntry(zipEntry);
151            FileUtils.copy(entryInputStream, zipOutputStream);
152        }
153        try {
154            entryInputStream.close();
155        } catch (IOException e) {
156            // NOP
157        }
158        zipOutputStream.closeEntry();
159    }
160
161}