+/*******************************************************************************
+ * 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));
+ }
+
+}
\ No newline at end of file