Skip to main content
Skip table of contents

High-level Annotation Flattening

Introduction

In our latest iText Core version 8 release, we’ve added a simple, yet highly configurable way to flatten annotations! It’s now much easier to flatten PDFs for further processing or archiving, as it can be done with just a couple of lines through the new PdfAnnotationFlattener (Java/.NET) class introduced in iText Core 8.0.2.

Here’s a quick example:

Default Flatten Example

Java
JAVA
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.utils.PdfAnnotationFlattener;

import java.io.File;

public class DefaultFlattenExample {
    private static final String SRC = "hello.pdf";
    private static final String DEST = "flat.pdf";

    public static void main(String[] args) throws Exception {
        File file = new File(DEST);
        file.getParentFile().mkdirs();

        new DefaultFlattenExample().manipulatePdf(DEST);
    }

    protected void manipulatePdf(String dest) throws Exception {
        try (PdfDocument pdf = new PdfDocument(new PdfReader(SRC), new PdfWriter(dest))) {
            PdfAnnotationFlattener pdfAnnotationFlattener = new PdfAnnotationFlattener();
            pdfAnnotationFlattener.flatten(pdf);
        }
    }
}
C#
C#
using iText.Kernel.Pdf;
using iText.Kernel.Utils;

public class DefaultFlattenExample
{
    public static readonly String SRC = "hello.pdf";
    public static readonly String DEST = "flat.pdf";

    public static void Main(String[] args)
    {
        FileInfo file = new FileInfo(DEST);
        file.Directory.Create();

        new DefaultFlattenExample().ManipulatePdf(DEST);
    }

    protected void ManipulatePdf(String dest)
    {
        using (PdfDocument pdf = new PdfDocument(new PdfReader(SRC), new PdfWriter(dest))) 
        {
            PdfAnnotationFlattener pdfAnnotationFlattener = new PdfAnnotationFlattener();
            pdfAnnotationFlattener.Flatten(pdf);
        }
    }
}

Default Flatten By List Example

The PdfAnnotationFlattener class also accepts a list of annotations to be flattened, which is useful if you would only like to flatten certain types of annotation. In this example, we can add only underline annotations to be flattened and the rest of the annotations will be left untouched.

Java
JAVA
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
import com.itextpdf.kernel.utils.PdfAnnotationFlattener;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class DefaultFlattenByListExample {
    private static final String SRC = "hello.pdf";
    private static final String DEST = "flat.pdf";

    public static void main(String[] args) throws Exception {
        File file = new File(DEST);
        file.getParentFile().mkdirs();

        new DefaultFlattenByListExample().manipulatePdf(DEST);
    }

    protected void manipulatePdf(String dest) throws Exception {
        try (PdfDocument pdf = new PdfDocument(new PdfReader(SRC), new PdfWriter(dest))){
            List<PdfAnnotation> annotationsToFlatten = new ArrayList<>();
            int pages = pdf.getNumberOfPages();
            for (int i = 1; i <= pages; i++) {
                PdfPage page = pdf.getPage(i);
                for (PdfAnnotation annotation : page.getAnnotations()) {

                    //In this example, we pass all underline annotations to be flattened only.

                    if(annotation.getSubtype().equals(PdfName.Underline)) {
                        annotationsToFlatten.add(annotation);
                    }
                }
            }
            PdfAnnotationFlattener pdfAnnotationFlattener = new PdfAnnotationFlattener();
            pdfAnnotationFlattener.flatten(annotationsToFlatten);
        }
    }
}
C#
C#
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Annot;
using iText.Kernel.Utils;

public class DefaultFlattenByListExample
{
    public static readonly String SRC = "hello.pdf";
    public static readonly String DEST = "flat.pdf";

    public static void Main(String[] args)
    {
        FileInfo file = new FileInfo(DEST);
        file.Directory.Create();

        new DefaultFlattenByListExample().ManipulatePdf(DEST);
    }

    protected void ManipulatePdf(String dest)
    {
        using (PdfDocument pdf = new PdfDocument(new PdfReader(SRC), new PdfWriter(dest)))
        {
            List<PdfAnnotation> annotationsToFlatten = new List<PdfAnnotation>();
            int pages = pdf.GetNumberOfPages();
            for (int i = 1; i <= pages; i++) {
                PdfPage page = pdf.GetPage(i);
                foreach (PdfAnnotation annotation in page.GetAnnotations()) {

                    //In this example, we pass all underline annotations to be flattened only.

                    if(annotation.GetSubtype().Equals(PdfName.Underline)) {
                        annotationsToFlatten.Add(annotation);
                    }
                }
            }
            PdfAnnotationFlattener pdfAnnotationFlattener = new PdfAnnotationFlattener();
            pdfAnnotationFlattener.Flatten(annotationsToFlatten);
        }
    }
}

Customized Flatten Example

That’s by no means all though. We also offer customizability options for our PdfAnnotationFlattener class through custom methods that can be passed to the factory, allowing for flexibility in the way specific widget types are flattened.

In this example, we can customize the flattener to draw all square annotations as blue rectangles for better visibility, instead of flattening them normally.

Java
JAVA
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
import com.itextpdf.kernel.pdf.annot.PdfSquareAnnotation;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.utils.PdfAnnotationFlattener;
import com.itextpdf.kernel.utils.annotationsflattening.IAnnotationFlattener;
import com.itextpdf.kernel.utils.annotationsflattening.PdfAnnotationFlattenFactory;

import java.io.File;

public class CustomizedFlattenExample {
    private static final String SRC = "hello.pdf";
    private static final String DEST = "flat.pdf";

    public static void main(String[] args) throws Exception {
        File file = new File(DEST);
        file.getParentFile().mkdirs();

        new CustomizedFlattenExample().manipulatePdf(DEST);
    }

    protected void manipulatePdf(String dest) throws Exception {
        try (PdfDocument pdf = new PdfDocument(new PdfReader(SRC), new PdfWriter(dest))) {
            PdfAnnotationFlattener pdfAnnotationFlattener = new PdfAnnotationFlattener(new PdfAnnotationFlattenFactory() {
                @Override
                public IAnnotationFlattener getAnnotationFlattenWorker(PdfName name) {
                
                    //In this example, we want to customize the flattener to draw all square
                    //annotations as blue rectangles regardless of the values in the annotation
                    
                    if (PdfName.Square.equals(name)) {
                        return new IAnnotationFlattener() {
                            @Override
                            public boolean flatten(PdfAnnotation pdfAnnotation, PdfPage pdfPage) {
                                PdfSquareAnnotation squareAnnotation = (PdfSquareAnnotation) pdfAnnotation;
                                final PdfCanvas under = new PdfCanvas(pdfPage.newContentStreamBefore(), pdfPage.getResources(),
                                        pdfPage.getDocument());
                                under.saveState()
                                        .setColor(ColorConstants.BLUE, true)
                                        .rectangle(squareAnnotation.getRectangle().toRectangle())
                                        .fill()
                                        .restoreState();
                                pdfPage.removeAnnotation(pdfAnnotation);
                                return true;
                            }
                        };
                    }
                    return super.getAnnotationFlattenWorker(name);
                }
            });
            pdfAnnotationFlattener.flatten(pdf);
        }
    }
}
C#
C#
using iText.Kernel.Colors;
using iText.Kernel.Pdf.Canvas;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Annot;
using iText.Kernel.Utils;
using iText.Kernel.Utils.Annotationsflattening;

public class CustomizedFlattenExample
{
    public static readonly String SRC = "hello.pdf";
    public static readonly String DEST = "flat.pdf";

    public static void Main(String[] args)
    {
        FileInfo file = new FileInfo(DEST);
        file.Directory.Create();

        new CustomizedFlattenExample().ManipulatePdf(DEST);
    }

    protected void ManipulatePdf(String dest)
    {
        using (PdfDocument pdf = new PdfDocument(new PdfReader(SRC), new PdfWriter(dest)))
        {
            PdfAnnotationFlattener pdfAnnotationFlattener = new PdfAnnotationFlattener(new CustomPdfAnnotationFlattenFactory());
            pdfAnnotationFlattener.Flatten(pdf);
        }
    }
}

public class CustomPdfAnnotationFlattenFactory : PdfAnnotationFlattenFactory
{
    public override IAnnotationFlattener GetAnnotationFlattenWorker(PdfName name)
    {    
        if (PdfName.Square.Equals(name))
        {
            return new CustomAnnotationFlattener();
        }
        return base.GetAnnotationFlattenWorker(name);
    }
}

public class CustomAnnotationFlattener : IAnnotationFlattener
{
    public bool Flatten(PdfAnnotation pdfAnnotation, PdfPage pdfPage)
    {
        //In this example, we want to customize the flattener to draw all square
        //annotations as blue rectangles regardless of the values in the annotation
    
        PdfSquareAnnotation squareAnnotation = (PdfSquareAnnotation) pdfAnnotation;
        PdfCanvas under = new PdfCanvas(pdfPage.NewContentStreamBefore(), pdfPage.GetResources(), 
            pdfPage.GetDocument());
        under.SaveState()
            .SetColor(ColorConstants.BLUE, true)
            .Rectangle(squareAnnotation.GetRectangle().ToRectangle())
            .Fill()
            .RestoreState();
        pdfPage.RemoveAnnotation(pdfAnnotation);
        return true;
    }
}

Here is the sample file used for these examples if you would like to try it yourself:

hello.pdf

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.