001/*
002 * (C) Copyright 2006-20012 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 */
018
019package org.nuxeo.template.odt;
020
021import java.io.ByteArrayInputStream;
022import java.io.File;
023import java.io.FileInputStream;
024import java.io.FileOutputStream;
025import java.io.IOException;
026import java.io.InputStream;
027import java.util.List;
028import java.util.zip.CRC32;
029import java.util.zip.ZipEntry;
030import java.util.zip.ZipOutputStream;
031
032import org.nuxeo.common.utils.FileUtils;
033import org.nuxeo.common.utils.ZipUtils;
034import org.nuxeo.ecm.core.api.Blob;
035
036/**
037 * Helper used to modify a ODT/Zip archive for addition Pictures (and potentially fragments)
038 *
039 * @author Tiry (tdelprat@nuxeo.com)
040 */
041public class OOoArchiveModifier {
042
043    public File updateArchive(File workingDir, File oooFile, List<Blob> blobs) throws IOException {
044        if (blobs == null || blobs.size() == 0) {
045            return oooFile;
046        }
047
048        File unzipDir = new File(workingDir, "unzip-" + oooFile.getName());
049        unzipDir.mkdirs();
050
051        ZipUtils.unzip(oooFile, unzipDir);
052
053        File pictureDirs = new File(unzipDir, "Pictures");
054        if (!pictureDirs.exists()) {
055            pictureDirs.mkdir();
056            pictureDirs = new File(unzipDir, "Pictures");
057        }
058
059        File contentDirs = new File(unzipDir, "Content");
060        if (!contentDirs.exists()) {
061            contentDirs.mkdir();
062            contentDirs = new File(unzipDir, "Content");
063        }
064
065        StringBuffer blobsManifest = new StringBuffer();
066        for (Blob blob : blobs) {
067            if (blob.getMimeType().startsWith("image")) {
068                FileUtils.copyToFile(blob.getStream(), new File(pictureDirs, blob.getFilename()));
069            } else {
070                FileUtils.copyToFile(blob.getStream(), new File(contentDirs, blob.getFilename()));
071            }
072
073            blobsManifest.append("<manifest:file-entry manifest:media-type=\"");
074            blobsManifest.append(blob.getMimeType());
075            if (blob.getMimeType().startsWith("image")) {
076                blobsManifest.append("\" manifest:full-path=\"Pictures/");
077            } else {
078                blobsManifest.append("\" manifest:full-path=\"Content/");
079            }
080            blobsManifest.append(blob.getFilename());
081            blobsManifest.append("\"/>\n");
082        }
083
084        File xmlManifestFile = new File(unzipDir.getPath() + "/META-INF/manifest.xml");
085        String xmlManifest = FileUtils.readFile(xmlManifestFile);
086        int idx = xmlManifest.indexOf("</manifest:manifest>");
087        xmlManifest = xmlManifest.substring(0, idx) + blobsManifest.toString() + xmlManifest.substring(idx);
088        FileUtils.writeFile(xmlManifestFile, xmlManifest.getBytes());
089
090        String path = oooFile.getAbsolutePath();
091
092        oooFile.delete();
093
094        oooFile = new File(path);
095        oooFile.createNewFile();
096
097        // ZipUtils.zip(unzipDir.listFiles(), oooFile);
098        mkOOoZip(unzipDir, oooFile);
099
100        FileUtils.deleteTree(unzipDir);
101
102        return oooFile;
103
104    }
105
106    protected void mkOOoZip(File directory, File outFile) throws IOException {
107
108        ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(outFile));
109
110        File manif = new File(directory, "mimetype");
111        writeOOoEntry(zipOutputStream, manif.getName(), manif, ZipEntry.STORED);
112
113        for (File fileEntry : directory.listFiles()) {
114            if (!fileEntry.getName().equals(manif.getName())) {
115                writeOOoEntry(zipOutputStream, fileEntry.getName(), fileEntry, ZipEntry.DEFLATED);
116            }
117        }
118
119        zipOutputStream.close();
120    }
121
122    protected void writeOOoEntry(ZipOutputStream zipOutputStream, String entryName, File fileEntry, int zipMethod) throws IOException {
123
124        if (fileEntry.isDirectory()) {
125            entryName = entryName + "/";
126            ZipEntry zentry = new ZipEntry(entryName);
127            zipOutputStream.putNextEntry(zentry);
128            zipOutputStream.closeEntry();
129            for (File child : fileEntry.listFiles()) {
130                writeOOoEntry(zipOutputStream, entryName + child.getName(), child, zipMethod);
131            }
132            return;
133        }
134
135        ZipEntry zipEntry = new ZipEntry(entryName);
136        InputStream entryInputStream = new FileInputStream(fileEntry);
137        zipEntry.setMethod(zipMethod);
138        if (zipMethod == ZipEntry.STORED) {
139            byte[] inputBytes = FileUtils.readBytes(entryInputStream);
140            CRC32 crc = new CRC32();
141            crc.update(inputBytes);
142            zipEntry.setCrc(crc.getValue());
143            zipEntry.setSize(inputBytes.length);
144            zipEntry.setCompressedSize(inputBytes.length);
145            zipOutputStream.putNextEntry(zipEntry);
146            FileUtils.copy(new ByteArrayInputStream(inputBytes), zipOutputStream);
147        } else {
148            zipOutputStream.putNextEntry(zipEntry);
149            FileUtils.copy(entryInputStream, zipOutputStream);
150        }
151        try {
152            entryInputStream.close();
153        } catch (IOException e) {
154            // NOP
155        }
156        zipOutputStream.closeEntry();
157    }
158
159}