Skip to content

Instantly share code, notes, and snippets.

@JoelGeraci-Datalogics
Created October 11, 2016 17:24
Show Gist options
  • Save JoelGeraci-Datalogics/2e1878f78812ed6b162f74204b24d722 to your computer and use it in GitHub Desktop.
Save JoelGeraci-Datalogics/2e1878f78812ed6b162f74204b24d722 to your computer and use it in GitHub Desktop.
Converts PDF Note Annotations to Page Content
/*
* Copyright Datalogics, Inc. 2015
*/
import com.adobe.fontengine.font.Font;
import com.adobe.internal.io.ByteReader;
import com.adobe.internal.io.InputStreamByteReader;
import com.adobe.pdfjt.core.types.ASName;
import com.adobe.pdfjt.pdf.content.Content;
import com.adobe.pdfjt.pdf.content.InstructionFactory;
import com.adobe.pdfjt.pdf.contentmodify.ContentWriter;
import com.adobe.pdfjt.pdf.contentmodify.ModifiableContent;
import com.adobe.pdfjt.pdf.document.PDFDocument;
import com.adobe.pdfjt.pdf.document.PDFOpenOptions;
import com.adobe.pdfjt.pdf.graphics.PDFRectangle;
import com.adobe.pdfjt.pdf.graphics.font.PDFFont;
import com.adobe.pdfjt.pdf.graphics.font.PDFFontSimple;
import com.adobe.pdfjt.pdf.interactive.annotation.PDFAnnotationEnum;
import com.adobe.pdfjt.pdf.interactive.annotation.PDFAnnotationList;
import com.adobe.pdfjt.pdf.interactive.annotation.PDFAnnotationMarkup;
import com.adobe.pdfjt.pdf.interactive.annotation.PDFAppearanceCharacteristics;
import com.adobe.pdfjt.pdf.page.PDFPage;
import com.adobe.pdfjt.pdf.page.PDFPageTree;
import com.adobe.pdfjt.services.ap.AppearanceService;
import com.adobe.pdfjt.services.ap.TextFormatterImpl;
import com.adobe.pdfjt.services.ap.spi.APContext;
import com.adobe.pdfjt.services.ap.spi.APResources;
import com.adobe.pdfjt.services.formflattener.FormFlattener;
import com.adobe.pdfjt.services.forms.FormFieldManager;
import com.adobe.pdfjt.services.forms.FormFieldService;
import com.adobe.pdfjt.services.forms.TextFieldOptions;
import com.datalogics.pdf.document.DocumentHelper;
import java.io.File;
import java.io.FileInputStream;
import java.util.EnumSet;
import java.util.HashMap;
public class SpeakerNotesToContent {
public static final String INPUT = "YOUR_INPUT_FILE_NAME";
public static final double additionalWidth = 2.5 * 72;
public static final double margin = .25 * 72;
static public void main(String[] args) throws Exception {
File file = new File(INPUT);
FileInputStream fis = new FileInputStream(file);
ByteReader byteReader = new InputStreamByteReader(fis);
PDFDocument pdfDocument = PDFDocument.newInstance(byteReader, PDFOpenOptions.newInstance());
// Run a few checks to be sure we have a file that was created by
// Acrobat from PowerPoint.
if (pdfDocument.getDocumentInfo().getCreator().contains("Acrobat PDFMaker")
&& pdfDocument.getDocumentInfo().getCreator().contains("PowerPoint")) {
PDFPageTree pages = pdfDocument.requirePages();
// Setup Form Field Manager
FormFieldManager formFieldManager = FormFieldService.getAcroFormFieldManager(pdfDocument);
TextFieldOptions textFieldOptions = new TextFieldOptions();
textFieldOptions.setFont(PDFFontSimple.newInstance(pdfDocument, ASName.k_Helvetica, ASName.k_Type1));
textFieldOptions.setFontColor(new double[] { 0, 0, 0 });
textFieldOptions.setFontSize(8);
textFieldOptions.setMultiLine(true);
PDFAppearanceCharacteristics pdfAppearanceCharacteristics = PDFAppearanceCharacteristics.newInstance(pdfDocument);
pdfAppearanceCharacteristics.setBackgroundColor(new double[] { 1, 1, 1 });
textFieldOptions.setAppearanceCharacteristics(pdfAppearanceCharacteristics);
// Iterate over the pages
for (int i = 0; i < pages.size(); i++) {
// reset page size to be wider by the "additionalWidth"
PDFPage page = pages.getPage(i);
PDFRectangle cropBox = page.getCropBox();
PDFRectangle newCropBox = PDFRectangle.newInstance(pdfDocument, 0, 0, cropBox.width() + additionalWidth, cropBox.height());
// Both the CropBox and the MediaBox must be set but they can be
// set to the same value
page.setCropBox(newCropBox);
page.setMediaBox(newCropBox);
// Create ContentWriter so we can add new drawing instructions
// to the page.
ContentWriter contentWriter = ContentWriter
.newInstance(ModifiableContent.newInstance(page.getContents(), page.getResources()));
// Add gray rectangle on right
contentWriter.write(InstructionFactory.newGSave());
contentWriter.write(InstructionFactory.newRectangle(cropBox.width(), 0, additionalWidth, cropBox.height()));
contentWriter.write(InstructionFactory.newColorFill(new double[] { .25, .25, .25 }));
contentWriter.write(InstructionFactory.newFillPath());
contentWriter.write(InstructionFactory.newGRestore());
// Add the line at the right edge of the presentation
contentWriter.write(InstructionFactory.newGSave());
contentWriter.write(InstructionFactory.newMoveTo(cropBox.width(), 0));
contentWriter.write(InstructionFactory.newLineTo(cropBox.width(), cropBox.height()));
contentWriter.write(InstructionFactory.newLineWidth(1));
contentWriter.write(InstructionFactory.newStrokePath());
contentWriter.write(InstructionFactory.newGRestore());
// Add shadow for white rectangle on right, use same position
// but offset by 3 points.
contentWriter.write(InstructionFactory.newGSave());
contentWriter.write(InstructionFactory.newRectangle(cropBox.width() - margin + 3, margin - 3, additionalWidth,
cropBox.height() - (margin * 2)));
contentWriter.write(InstructionFactory.newColorFill(new double[] { 0, 0, 0 }));
contentWriter.write(InstructionFactory.newFillPath());
contentWriter.write(InstructionFactory.newGRestore());
// Add white rectangle on right
contentWriter.write(InstructionFactory.newGSave());
contentWriter.write(InstructionFactory.newRectangle(cropBox.width() - margin, 0 + margin, additionalWidth,
cropBox.height() - (margin * 2)));
contentWriter.write(InstructionFactory.newColorFill(new double[] { 1, 1, 1 }));
contentWriter.write(InstructionFactory.newColorStroke(new double[] { 0, 0, 0 }));
contentWriter.write(InstructionFactory.newLineWidth(1));
contentWriter.write(InstructionFactory.newCloseFillAndStrokePath());
contentWriter.write(InstructionFactory.newGRestore());
// Add content to page
Content newContent = contentWriter.close();
page.getContents().addContents(newContent.getContentStream());
PDFAnnotationList annotationList = page.getAnnotationList();
// Run a few checks to be sure the first annotation is the one
// we're expecting.
if (annotationList != null && annotationList.size() == 1 && annotationList.get(0).getSubtype() == ASName.k_Text) {
PDFAnnotationMarkup speakerTextNote = (PDFAnnotationMarkup) annotationList.get(0);
if (speakerTextNote.getTitle().matches("Presenter")) {
String speakerTextNoteText = "Slide Notes:\r\n\r\n" + speakerTextNote.getContents();
// remove the annotation
annotationList.remove(speakerTextNote);
// Add a text field whose value is the same as the
// content
// of the annotation added by Acrobat and using the text
// field options defined above.
formFieldManager.addTextField("PresenterNotePage_" + page.getPageNumber(), speakerTextNoteText,
PDFRectangle.newInstance(pdfDocument, cropBox.width(), (margin * 2),
cropBox.width() + additionalWidth - (margin * 2), cropBox.height() - (margin * 2)),
page, textFieldOptions);
}
}
}
// Set up the Appearance Context so we can generate the appearances
// of the text fields.
APResources apResources = new APResources(pdfDocument.getCosDocument().getOptions().getFontSet(),
pdfDocument.getCosDocument().getOptions().getDocLocale(), new HashMap<Font, PDFFont>());
APContext apContext = new APContext(apResources, true, null);
apContext.setAnnotationsToBeProcessed(EnumSet.of(PDFAnnotationEnum.Widget));
// Generate appearances for the newly added text fields then flatten
// them.
AppearanceService.generateAppearances(pdfDocument, apContext, new TextFormatterImpl(pdfDocument));
FormFlattener.flattenDocument(apContext, pdfDocument, new TextFormatterImpl(pdfDocument));
DocumentHelper.saveFullAndClose(pdfDocument, INPUT.replace("input", "output").replace(".pdf", "_Notes.pdf"));
} else {
System.out.println("Not a PDF from PowerPoint");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment