Java tutorial
/* * Created on 22-Oct-2007 * Copyright (C) 2007 by Andrea Vacondio. * * * This library is provided under dual licenses. * You may choose the terms of the Lesser General Public License version 2.1 or the General Public License version 2 * License at your discretion. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; * either version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.pdfsam.console.business.pdf.handlers; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.TreeSet; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.dom4j.Node; import org.dom4j.io.SAXReader; import org.pdfsam.console.business.ConsoleServicesFacade; import org.pdfsam.console.business.dto.WorkDoneDataModel; import org.pdfsam.console.business.dto.commands.AbstractParsedCommand; import org.pdfsam.console.business.dto.commands.SplitParsedCommand; import org.pdfsam.console.business.pdf.bookmarks.BookmarksProcessor; import org.pdfsam.console.business.pdf.handlers.interfaces.AbstractCmdExecutor; import org.pdfsam.console.exceptions.console.ConsoleException; import org.pdfsam.console.exceptions.console.SplitException; import org.pdfsam.console.utils.FileUtility; import org.pdfsam.console.utils.PdfUtility; import org.pdfsam.console.utils.perfix.FileNameRequest; import org.pdfsam.console.utils.perfix.PrefixParser; import com.lowagie.text.Document; import com.lowagie.text.pdf.PdfImportedPage; import com.lowagie.text.pdf.PdfReader; import com.lowagie.text.pdf.PdfSmartCopy; import com.lowagie.text.pdf.RandomAccessFileOrArray; import com.lowagie.text.pdf.SimpleBookmark; /** * Command executor for the split command * * @author Andrea Vacondio */ public class SplitCmdExecutor extends AbstractCmdExecutor { private static final Logger LOG = Logger.getLogger(SplitCmdExecutor.class.getPackage().getName()); private PrefixParser prefixParser; private PdfSmartCopy pdfWriter = null; private PdfReader pdfReader = null; public void execute(AbstractParsedCommand parsedCommand) throws ConsoleException { if ((parsedCommand != null) && (parsedCommand instanceof SplitParsedCommand)) { SplitParsedCommand inputCommand = (SplitParsedCommand) parsedCommand; setPercentageOfWorkDone(0); try { prefixParser = new PrefixParser(inputCommand.getOutputFilesPrefix(), inputCommand.getInputFile().getFile().getName()); if (SplitParsedCommand.S_BURST.equals(inputCommand.getSplitType())) { executeBurst(inputCommand); } else if (SplitParsedCommand.S_NSPLIT.equals(inputCommand.getSplitType())) { executeNSplit(inputCommand); } else if (SplitParsedCommand.S_SPLIT.equals(inputCommand.getSplitType())) { executeSplit(inputCommand); } else if (SplitParsedCommand.S_EVEN.equals(inputCommand.getSplitType()) || SplitParsedCommand.S_ODD.equals(inputCommand.getSplitType())) { executeSplitOddEven(inputCommand); } else if (SplitParsedCommand.S_SIZE.equals(inputCommand.getSplitType())) { executeSizeSplit(inputCommand); } else if (SplitParsedCommand.S_BLEVEL.equals(inputCommand.getSplitType())) { executeBookmarksSplit(inputCommand); } else { throw new SplitException(SplitException.ERR_NOT_VALID_SPLIT_TYPE, new String[] { inputCommand.getSplitType() }); } } catch (Exception e) { throw new SplitException(e); } finally { setWorkCompleted(); } } else { throw new ConsoleException(ConsoleException.ERR_BAD_COMMAND); } } /** * Execute the split of a pdf document when split type is S_BURST * * @param inputCommand * @throws Exception */ private void executeBurst(SplitParsedCommand inputCommand) throws Exception { int currentPage; Document currentDocument; pdfReader = PdfUtility.readerFor(inputCommand.getInputFile()); pdfReader.removeUnusedObjects(); pdfReader.consolidateNamedDestinations(); // we retrieve the total number of pages int n = pdfReader.getNumberOfPages(); int fileNum = 0; LOG.info("Found " + n + " pages in input pdf document."); for (currentPage = 1; currentPage <= n; currentPage++) { LOG.debug("Creating a new document."); fileNum++; File tmpFile = FileUtility.generateTmpFile(inputCommand.getOutputFile()); FileNameRequest request = new FileNameRequest(currentPage, fileNum, null); File outFile = new File(inputCommand.getOutputFile().getCanonicalPath(), prefixParser.generateFileName(request)); currentDocument = new Document(pdfReader.getPageSizeWithRotation(currentPage)); pdfWriter = new PdfSmartCopy(currentDocument, new FileOutputStream(tmpFile)); currentDocument.addCreator(ConsoleServicesFacade.CREATOR); setCompressionSettingOnWriter(inputCommand, pdfWriter); // set pdf version setPdfVersionSettingOnWriter(inputCommand, pdfWriter, Character.valueOf(pdfReader.getPdfVersion())); currentDocument.open(); PdfImportedPage importedPage = pdfWriter.getImportedPage(pdfReader, currentPage); pdfWriter.addPage(importedPage); currentDocument.close(); FileUtility.renameTemporaryFile(tmpFile, outFile, inputCommand.isOverwrite()); LOG.debug("File " + outFile.getCanonicalPath() + " created."); setPercentageOfWorkDone((currentPage * WorkDoneDataModel.MAX_PERGENTAGE) / n); } pdfReader.close(); LOG.info("Burst done."); } /** * Execute the split of a pdf document when split type is S_ODD or S_EVEN * * @param inputCommand * @throws Exception */ private void executeSplitOddEven(SplitParsedCommand inputCommand) throws Exception { pdfReader = PdfUtility.readerFor(inputCommand.getInputFile()); pdfReader.removeUnusedObjects(); pdfReader.consolidateNamedDestinations(); // we retrieve the total number of pages int n = pdfReader.getNumberOfPages(); int fileNum = 0; LOG.info("Found " + n + " pages in input pdf document."); int currentPage; Document currentDocument = new Document(pdfReader.getPageSizeWithRotation(1)); boolean isTimeToClose = false; PdfImportedPage importedPage; File tmpFile = null; File outFile = null; for (currentPage = 1; currentPage <= n; currentPage++) { // check if i've to read one more page or to open a new doc isTimeToClose = ((currentPage != 1) && ((SplitParsedCommand.S_ODD.equals(inputCommand.getSplitType()) && ((currentPage % 2) != 0)) || (SplitParsedCommand.S_EVEN.equals(inputCommand.getSplitType()) && ((currentPage % 2) == 0)))); if (!isTimeToClose) { LOG.debug("Creating a new document."); fileNum++; tmpFile = FileUtility.generateTmpFile(inputCommand.getOutputFile()); FileNameRequest request = new FileNameRequest(currentPage, fileNum, null); outFile = new File(inputCommand.getOutputFile(), prefixParser.generateFileName(request)); currentDocument = new Document(pdfReader.getPageSizeWithRotation(currentPage)); pdfWriter = new PdfSmartCopy(currentDocument, new FileOutputStream(tmpFile)); // set creator currentDocument.addCreator(ConsoleServicesFacade.CREATOR); setCompressionSettingOnWriter(inputCommand, pdfWriter); setPdfVersionSettingOnWriter(inputCommand, pdfWriter, Character.valueOf(pdfReader.getPdfVersion())); currentDocument.open(); } importedPage = pdfWriter.getImportedPage(pdfReader, currentPage); pdfWriter.addPage(importedPage); // if it's time to close the document if ((isTimeToClose) || (currentPage == n) || ((currentPage == 1) && (SplitParsedCommand.S_ODD.equals(inputCommand.getSplitType())))) { currentDocument.close(); FileUtility.renameTemporaryFile(tmpFile, outFile, inputCommand.isOverwrite()); LOG.debug("File " + outFile.getCanonicalPath() + " created."); } setPercentageOfWorkDone((currentPage * WorkDoneDataModel.MAX_PERGENTAGE) / n); } pdfReader.close(); LOG.info("Split " + inputCommand.getSplitType() + " done."); } /** * Execute the split of a pdf document when split type is S_SPLIT or S_NSPLIT * * @param inputCommand * @throws Exception */ private void executeSplit(SplitParsedCommand inputCommand) throws Exception { executeSplit(inputCommand, null); } /** * Execute the split of a pdf document when split type is S_BLEVEL * * @param inputCommand * @param bookmarksTable * bookmarks table. It's populated only when splitting by bookmarks. If null or empty it's ignored * @throws Exception */ private void executeSplit(SplitParsedCommand inputCommand, Hashtable bookmarksTable) throws Exception { pdfReader = PdfUtility.readerFor(inputCommand.getInputFile()); pdfReader.removeUnusedObjects(); pdfReader.consolidateNamedDestinations(); int n = pdfReader.getNumberOfPages(); BookmarksProcessor bookmarkProcessor = new BookmarksProcessor(SimpleBookmark.getBookmark(pdfReader), n); int fileNum = 0; LOG.info("Found " + n + " pages in input pdf document."); Integer[] limits = inputCommand.getSplitPageNumbers(); // limits list validation end clean TreeSet limitsList = validateSplitLimits(limits, n); if (limitsList.isEmpty()) { throw new SplitException(SplitException.ERR_NO_PAGE_LIMITS); } // HERE I'M SURE I'VE A LIMIT LIST WITH VALUES, I CAN START BOOKMARKS int currentPage; Document currentDocument = new Document(pdfReader.getPageSizeWithRotation(1)); int relativeCurrentPage = 0; int endPage = n; int startPage = 1; PdfImportedPage importedPage; File tmpFile = null; File outFile = null; Iterator itr = limitsList.iterator(); if (itr.hasNext()) { endPage = ((Integer) itr.next()).intValue(); } for (currentPage = 1; currentPage <= n; currentPage++) { relativeCurrentPage++; // check if i've to read one more page or to open a new doc if (relativeCurrentPage == 1) { LOG.debug("Creating a new document."); fileNum++; tmpFile = FileUtility.generateTmpFile(inputCommand.getOutputFile()); String bookmark = null; if (bookmarksTable != null && bookmarksTable.size() > 0) { bookmark = (String) bookmarksTable.get(new Integer(currentPage)); } FileNameRequest request = new FileNameRequest(currentPage, fileNum, bookmark); outFile = new File(inputCommand.getOutputFile(), prefixParser.generateFileName(request)); startPage = currentPage; currentDocument = new Document(pdfReader.getPageSizeWithRotation(currentPage)); pdfWriter = new PdfSmartCopy(currentDocument, new FileOutputStream(tmpFile)); // set creator currentDocument.addCreator(ConsoleServicesFacade.CREATOR); setCompressionSettingOnWriter(inputCommand, pdfWriter); setPdfVersionSettingOnWriter(inputCommand, pdfWriter, Character.valueOf(pdfReader.getPdfVersion())); currentDocument.open(); } importedPage = pdfWriter.getImportedPage(pdfReader, currentPage); pdfWriter.addPage(importedPage); // if it's time to close the document if (currentPage == endPage) { LOG.info("Temporary document " + tmpFile.getName() + " done, now adding bookmarks..."); // manage bookmarks List bookmarks = bookmarkProcessor.processBookmarks(startPage, endPage); if (bookmarks != null) { pdfWriter.setOutlines(bookmarks); } relativeCurrentPage = 0; currentDocument.close(); FileUtility.renameTemporaryFile(tmpFile, outFile, inputCommand.isOverwrite()); LOG.debug("File " + outFile.getCanonicalPath() + " created."); endPage = (itr.hasNext()) ? ((Integer) itr.next()).intValue() : n; } setPercentageOfWorkDone((currentPage * WorkDoneDataModel.MAX_PERGENTAGE) / n); } pdfReader.close(); LOG.info("Split " + inputCommand.getSplitType() + " done."); } /** * Execute the split of a pdf document when split type is S_NSPLIT * * @param inputCommand * @throws Exception */ private void executeNSplit(SplitParsedCommand inputCommand) throws Exception { Integer[] numberPages = inputCommand.getSplitPageNumbers(); if (numberPages != null && numberPages.length == 1) { pdfReader = PdfUtility.readerFor(inputCommand.getInputFile()); pdfReader.removeUnusedObjects(); pdfReader.consolidateNamedDestinations(); int n = pdfReader.getNumberOfPages(); int numberPage = numberPages[0].intValue(); if (numberPage < 1 || numberPage > n) { pdfReader.close(); throw new SplitException(SplitException.ERR_NO_SUCH_PAGE, new String[] { "" + numberPage }); } else { ArrayList retVal = new ArrayList(); for (int i = numberPage; i < n; i += numberPage) { retVal.add(new Integer(i)); } inputCommand.setSplitPageNumbers((Integer[]) retVal.toArray(new Integer[0])); } pdfReader.close(); } executeSplit(inputCommand); } /** * Execute the split of a pdf document when split type is S_BLEVEL * * @param inputCommand * @throws Exception */ private void executeBookmarksSplit(SplitParsedCommand inputCommand) throws Exception { pdfReader = PdfUtility.readerFor(inputCommand.getInputFile()); int bLevel = inputCommand.getBookmarksLevel().intValue(); Hashtable bookmarksTable = new Hashtable(); if (bLevel > 0) { pdfReader.removeUnusedObjects(); pdfReader.consolidateNamedDestinations(); List bookmarks = SimpleBookmark.getBookmark(pdfReader); ByteArrayOutputStream out = new ByteArrayOutputStream(); SimpleBookmark.exportToXML(bookmarks, out, "UTF-8", false); ByteArrayInputStream input = new ByteArrayInputStream(out.toByteArray()); int maxDepth = PdfUtility.getMaxBookmarksDepth(input); input.reset(); if (bLevel <= maxDepth) { SAXReader reader = new SAXReader(); org.dom4j.Document document = reader.read(input); // head node String headBookmarkXQuery = "/Bookmark/Title[@Action=\"GoTo\"]"; Node headNode = document.selectSingleNode(headBookmarkXQuery); if (headNode != null && headNode.getText() != null && headNode.getText().trim().length() > 0) { bookmarksTable.put(new Integer(1), headNode.getText().trim()); } // bLevel nodes StringBuffer buffer = new StringBuffer("/Bookmark"); for (int i = 0; i < bLevel; i++) { buffer.append("/Title[@Action=\"GoTo\"]"); } String xQuery = buffer.toString(); List nodes = document.selectNodes(xQuery); input.close(); input = null; if (nodes != null && nodes.size() > 0) { LinkedHashSet pageSet = new LinkedHashSet(nodes.size()); for (Iterator nodeIter = nodes.iterator(); nodeIter.hasNext();) { Node currentNode = (Node) nodeIter.next(); Node pageAttribute = currentNode.selectSingleNode("@Page"); if (pageAttribute != null && pageAttribute.getText().length() > 0) { String attribute = pageAttribute.getText(); int blankIndex = attribute.indexOf(' '); if (blankIndex > 0) { Integer currentNumber = new Integer(attribute.substring(0, blankIndex)); String bookmarkText = currentNode.getText().trim(); // fix #2789963 if (currentNumber.intValue() > 0) { // bookmarks regexp matching if any if (StringUtils.isBlank(inputCommand.getBookmarkRegexp()) || bookmarkText.matches(inputCommand.getBookmarkRegexp())) { // to split just before the given page if ((currentNumber.intValue()) > 1) { pageSet.add(new Integer(currentNumber.intValue() - 1)); } if (StringUtils.isNotBlank(bookmarkText)) { bookmarksTable.put(currentNumber, bookmarkText.trim()); } } } } } } if (pageSet.size() > 0) { if (StringUtils.isBlank(inputCommand.getBookmarkRegexp())) { LOG.debug("Found " + pageSet.size() + " destination pages at level " + bLevel); } else { LOG.debug("Found " + pageSet.size() + " destination pages at level " + bLevel + " matching '" + inputCommand.getBookmarkRegexp() + "'"); } inputCommand.setSplitPageNumbers((Integer[]) pageSet.toArray(new Integer[pageSet.size()])); } else { throw new SplitException(SplitException.ERR_BLEVEL_NO_DEST, new String[] { "" + bLevel }); } } else { throw new SplitException(SplitException.ERR_BLEVEL, new String[] { "" + bLevel }); } } else { input.close(); pdfReader.close(); throw new SplitException(SplitException.ERR_BLEVEL_OUTOFBOUNDS, new String[] { "" + bLevel, "" + maxDepth }); } } else { pdfReader.close(); throw new SplitException(SplitException.ERR_NOT_VALID_BLEVEL, new String[] { "" + bLevel }); } pdfReader.close(); executeSplit(inputCommand, bookmarksTable); } /** * Execute the split of a pdf document when split type is S_SIZE * * @param inputCommand * @throws Exception */ private void executeSizeSplit(SplitParsedCommand inputCommand) throws Exception { pdfReader = PdfUtility.readerFor(inputCommand.getInputFile()); pdfReader.removeUnusedObjects(); pdfReader.consolidateNamedDestinations(); int n = pdfReader.getNumberOfPages(); BookmarksProcessor bookmarkProcessor = new BookmarksProcessor(SimpleBookmark.getBookmark(pdfReader), n); int fileNum = 0; LOG.info("Found " + n + " pages in input pdf document."); int currentPage; Document currentDocument = new Document(pdfReader.getPageSizeWithRotation(1)); PdfImportedPage importedPage; File tmpFile = null; File outFile = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); int startPage = 0; int relativeCurrentPage = 0; for (currentPage = 1; currentPage <= n; currentPage++) { relativeCurrentPage++; // time to open a new document? if (relativeCurrentPage == 1) { LOG.debug("Creating a new document."); startPage = currentPage; fileNum++; tmpFile = FileUtility.generateTmpFile(inputCommand.getOutputFile()); FileNameRequest request = new FileNameRequest(currentPage, fileNum, null); outFile = new File(inputCommand.getOutputFile(), prefixParser.generateFileName(request)); currentDocument = new Document(pdfReader.getPageSizeWithRotation(currentPage)); baos = new ByteArrayOutputStream(); pdfWriter = new PdfSmartCopy(currentDocument, baos); // set creator currentDocument.addCreator(ConsoleServicesFacade.CREATOR); setCompressionSettingOnWriter(inputCommand, pdfWriter); setPdfVersionSettingOnWriter(inputCommand, pdfWriter, Character.valueOf(pdfReader.getPdfVersion())); currentDocument.open(); } importedPage = pdfWriter.getImportedPage(pdfReader, currentPage); pdfWriter.addPage(importedPage); // if it's time to close the document if ((currentPage == n) || ((relativeCurrentPage > 1) && ((baos.size() / relativeCurrentPage) * (1 + relativeCurrentPage) > inputCommand.getSplitSize().longValue()))) { LOG.debug("Current stream size: " + baos.size() + " bytes."); // manage bookmarks List bookmarks = bookmarkProcessor.processBookmarks(startPage, currentPage); if (bookmarks != null) { pdfWriter.setOutlines(bookmarks); } relativeCurrentPage = 0; currentDocument.close(); FileOutputStream fos = new FileOutputStream(tmpFile); baos.writeTo(fos); fos.close(); baos.close(); LOG.info("Temporary document " + tmpFile.getName() + " done."); FileUtility.renameTemporaryFile(tmpFile, outFile, inputCommand.isOverwrite()); LOG.debug("File " + outFile.getCanonicalPath() + " created."); } setPercentageOfWorkDone((currentPage * WorkDoneDataModel.MAX_PERGENTAGE) / n); } pdfReader.close(); LOG.info("Split " + inputCommand.getSplitType() + " done."); } /** * Validate the limit list in "split after these pages" * * @param limits * limits array containing page numbers * @param upperLimit * max page number * @return purged list */ private TreeSet validateSplitLimits(Integer[] limits, int upperLimit) { TreeSet limitsList = new TreeSet(); /* * I check if it contains zero values or double values and i remove them */ for (int j = 0; j < limits.length; j++) { if ((limits[j].intValue() <= 0) || (limits[j].intValue() >= upperLimit)) { LOG.warn("Cannot split before page number " + limits[j].intValue() + ", limit removed."); } else { if (!limitsList.add(limits[j])) { LOG.warn( limits[j].intValue() + " found more than once in the page limits list, limit removed."); } } } return limitsList; } public void clean() { closePdfReader(pdfReader); closePdfWriter(pdfWriter); } }