/******************************************************************************* * Copyright (c) 2017 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Semantum Oy - initial API and implementation *******************************************************************************/ package org.simantics.export.core.pdf; import java.awt.geom.Point2D; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.Element; import com.lowagie.text.Font; import com.lowagie.text.Phrase; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.BadPdfFormatException; import com.lowagie.text.pdf.ColumnText; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfCopy; import com.lowagie.text.pdf.PdfImportedPage; import com.lowagie.text.pdf.PdfReader; import com.lowagie.text.pdf.PdfWriter; /** * PDF page numbering related post-processing utilities using iText. * * @author Tuukka Lehtonen * @since 1.28.0 */ public class PageNumbering { public static enum NumberingFormat { PAGE_SLASH_TOTAL_PAGES } public static enum Position { BOTTOM_LEFT, BOTTOM_RIGHT, TOP_LEFT, TOP_RIGHT, } private static int pageNumberAlignment(Position positioning) { switch (positioning) { case BOTTOM_LEFT: case TOP_LEFT: return Element.ALIGN_LEFT; case BOTTOM_RIGHT: case TOP_RIGHT: default: return Element.ALIGN_RIGHT; } } private static Point2D.Float pageNumberPosition(Position positioning, Rectangle pageSize, Font font) { switch (positioning) { case TOP_LEFT: return new Point2D.Float(12, pageSize.getHeight() - 12 - font.getCalculatedSize()*0.8f); case TOP_RIGHT: return new Point2D.Float(pageSize.getWidth() - 12, pageSize.getHeight() - 12 - font.getCalculatedSize()*0.8f); case BOTTOM_LEFT: return new Point2D.Float(12, 12); case BOTTOM_RIGHT: default: return new Point2D.Float(pageSize.getWidth() - 12, 12); } } public static String formatPageNumber(NumberingFormat format, int pageNumber, int totalPages) { switch (format) { case PAGE_SLASH_TOTAL_PAGES: return String.format("%d / %d", pageNumber, totalPages); default: throw new UnsupportedOperationException("Unsupported numbering format: " + format); } } public static void addPageNumber( PdfCopy pdfCopy, PdfReader sourceReader, int sourcePageNumber, int currentPageNumber, int totalPages, Font font, Position positioning, NumberingFormat format) throws IOException, BadPdfFormatException { Rectangle pageSize = sourceReader.getPageSizeWithRotation(sourcePageNumber); PdfImportedPage imp = pdfCopy.getImportedPage(sourceReader, sourcePageNumber); PdfCopy.PageStamp ps = pdfCopy.createPageStamp(imp); PdfContentByte over = ps.getOverContent(); String text = formatPageNumber(format, currentPageNumber, totalPages); Point2D.Float pos = pageNumberPosition(positioning, pageSize, font); int alignment = pageNumberAlignment(positioning); ColumnText.showTextAligned(over, alignment, new Phrase(text, font), pos.x, pos.y, 0); ps.alterContents(); pdfCopy.addPage(imp); } public static void addPageNumbers( IProgressMonitor monitor, Path inputFile, Path outputFile, Position positioning, NumberingFormat format, Font pageNumberFont) throws IOException, DocumentException { PdfReader reader = null; try { reader = new PdfReader(inputFile.toString()); int totalPages = reader.getNumberOfPages(); int currentPage = 1; SubMonitor mon = SubMonitor.convert(monitor, totalPages); try (OutputStream fos = Files.newOutputStream(outputFile)) { Document document = new Document(); PdfCopy pdfCopy = new PdfCopy(document, fos); pdfCopy.setPdfVersion(PdfWriter.PDF_VERSION_1_7); document.open(); try { for (int i = 1; i <= totalPages; ++i) { mon.subTask(i + "/" + totalPages); PageNumbering.addPageNumber(pdfCopy, reader, i, currentPage++, totalPages, pageNumberFont, positioning, format); } } finally { if (document != null && document.isOpen()) document.close(); if (pdfCopy != null) pdfCopy.close(); } } } finally { if (reader != null) reader.close(); } } public static void addPageNumbers( IProgressMonitor monitor, Path inputFile, Path outputFile, Position positioning, NumberingFormat format) throws IOException, DocumentException { addPageNumbers(monitor, inputFile, outputFile, positioning, format, new Font(Font.HELVETICA, 8)); } }