Adding PDFStamper overlay causes XFA pre-populated fields to disappear - java

The SRC_PDF I'm using was previously filled by a previous method using the following code:
XfaForm xfa = form.getXfa();
xfa.fillXfaForm(new FileInputStream(XML));
stamper.close();
reader.close();
Now I'd like to create an image overlay on the 2nd page of the filled document.
By using the PDFStamper (even with the use of append), I now lose all my filled fields but do get the image overlay on page 2. Has someone figured out how to do an image overlay on a prepopulated XFA PDF form?
package example.pdf;
import java.io.FileOutputStream;
import org.junit.Test;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
public class SignaturePDFTest {
public static final String SRC_PDF = "target/XFA_Form_filled.pdf";
public static final String DEST_PDF = "target/XFA_Form_withImageOverlay.pdf";
public static final String OVERLAY_GRAPHIC = "src/test/imageOverlay.png";
#Test
public void testPdfStamp() throws Exception
{
PdfReader pdfReader = new PdfReader(SRC_PDF);
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileOutputStream(DEST_PDF), '\0', true);
Image image = Image.getInstance(OVERLAY_GRAPHIC);
image.scaleToFit(200, 170);
for (int i=1; i<=pdfReader.getNumberOfPages(); i++)
{
PdfContentByte content = pdfStamper.getOverContent(i);
image.setAbsolutePosition(450f, 450f);
if (i==2) content.addImage(image);
}
pdfStamper.close();
pdfReader.close();
}
}
After I run the pdfStamper overlay I lose the filled fields from the previous calls: xfa.fillXfaForm(new FileInputStream(XML)); Is there a specific way to place an image on a specific page when the PDF form has XFA fields?

Related

PDFBox2 : Acrobat consider my annotation added between two signature as modified, why ? and how to solve this?

I'm trying to do this use case:
I start with a document that contains a digital signature (our provider is using DCC https://github.com/esig/dss which is based on PDFBox...)
with PDFBox 2.0.27 I added an annotation using incremental save (I used different kind of annotation, free text, stamp as text, stamp with image…)
I send this document for a new digital sign
Actual result :
When I open my document with adobe acrobat reader, I click on validate all in the signature panel and acrobat claims that my annotation was changed
The stamp annotation object is correctly set and is not provided in another incremental save, I don't know why Adobe claims it was changed.
Link to the final PDF : https://drive.google.com/file/d/1CV7lJ8dY1gDcx3rgHoW5yHJ612JSAK9B/view?usp=share_link
Expected result : When I open the document in acrobat, I can see the annotation, in the signature panel everything is green, the annotations are not marked as modified EVEN after clicking on "Validate ALL"
This is the code source :
import org.apache.pdfbox.pdmodel.PDAppearanceContentStream;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationRubberStamp;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.util.Matrix;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
public class PDFAnnotStampText {
public static void main(String[] args) {
//Creating PDF document object
PDDocument document = null;
String filename = "20221217-VisibleSign-BeforeAnnot-02";
try {
document = PDDocument.load(new File("testfiles/" + filename + ".pdf"));
document.getClass();
if (!document.isEncrypted()) {
System.out.println("not encrypted");
// https://stackoverflow.com/questions/65927280/annotation-content-not-appearing-with-pdfbox
OutputStream resultFile = new FileOutputStream("testfiles/" + filename + "_stamptxt.pdf");
PDPage page = (PDPage) document.getPage(0);
addAnnotation("annotation name", document, page, 50, 500, "Annot powered");
document.saveIncremental(resultFile);
document.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static void addAnnotation(String name, PDDocument doc, PDPage page, float x, float y, String text) throws IOException {
List<PDAnnotation> annotations = page.getAnnotations();
PDAnnotationRubberStamp t = new PDAnnotationRubberStamp();
t.setAnnotationName(name); // might play important role
t.setPrinted(true); // always visible
t.setReadOnly(true); // does not interact with user
t.setContents(text);
// calculate realWidth, realHeight according to font size (e.g. using _font.getStringWidth(text))
float realWidth = 100, realHeight = 15;
PDRectangle rect = new PDRectangle(x, y, realWidth, realHeight);
t.setRectangle(rect);
PDAppearanceDictionary ap = new PDAppearanceDictionary();
ap.setNormalAppearance(createAppearanceStream(doc, t));
t.setAppearance(ap);
annotations.add(t);
page.setAnnotations(annotations);
// these must be set for incremental save to work properly (PDFBOX < 3.0.0 at least?)
ap.getCOSObject().setNeedToBeUpdated(true);
t.getCOSObject().setNeedToBeUpdated(true);
page.getResources().getCOSObject().setNeedToBeUpdated(true);
page.getCOSObject().setNeedToBeUpdated(true);
doc.getDocumentCatalog().getPages().getCOSObject().setNeedToBeUpdated(true);
doc.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true);
}
private static void modifyAppearanceStream(PDAppearanceStream aps, PDAnnotation ann) throws IOException {
PDAppearanceContentStream apsContent = null;
try {
PDRectangle rect = ann.getRectangle();
rect = new PDRectangle(0, 0, rect.getWidth(), rect.getHeight()); // need to be relative - this is mega important because otherwise it appears as if nothing is printed
aps.setBBox(rect); // set bounding box to the dimensions of the annotation itself
// embed our unicode font (NB: yes, this needs to be done otherwise aps.getResources() == null which will cause NPE later during setFont)
PDResources res = new PDResources();
PDFont font = PDType1Font.HELVETICA_BOLD;
res.add(font).getName(); // okay I create _font elsewhere
aps.setResources(res);
// draw directly on the XObject's content stream
apsContent = new PDAppearanceContentStream(aps);
apsContent.beginText();
apsContent.setFont(PDType1Font.HELVETICA_BOLD, 12); // _font
apsContent.setTextMatrix(Matrix.getTranslateInstance(0, 1));
apsContent.showText(ann.getContents());
apsContent.endText();
}
finally {
if (apsContent != null) {
try {
apsContent.close();
} catch (Exception ex) {
System.out.println(ex);
}
}
}
aps.getResources().getCOSObject().setNeedToBeUpdated(true);
aps.getCOSObject().setNeedToBeUpdated(true);
}
private static PDAppearanceStream createAppearanceStream(final PDDocument document, PDAnnotation ann) throws IOException
{
PDAppearanceStream aps = new PDAppearanceStream(document);
modifyAppearanceStream(aps, ann);
return aps;
}
}
As told in the comments, the Adobe signed PDF (thank you Dimitar) included the stamp annotation in the last incremental save (the DSS signed PDF didn't) and added two things: 1) /P 14 0 R and 2) /NM (495f9a95-136e-4992-881a-6a834fa85519). Maybe Adobe adds these itself quietly and then complains about its own behavior, despite that both entries are optional.
Calling t.setPage(page) in the code from the question solved the problem.
I've also submitted a bug report on this semi-official site.

How to add SVG image to XSSFWorkBook

While trying to add an SVG image to XSSFWorkbook, I noticed there is no option to set the PictureType to SVG.
FileInputStream in = new FileInputStream("testWorkbook.xlsx");
XSSFWorkbook wb = (XSSFWorkbook)WorkbookFactory.create(in);
InputStream inputStream = new FileInputStream("/home/test/test1.svg");
//Get the contents of an InputStream as a byte[].
byte[] imageData = IOUtils.toByteArray(inputStream);
// XSSFWorkbook.PICTURE_TYPE_SVG is not supported yet.
int pictureIndex = wb.addPicture(imageData, XSSFWorkbook.PICTURE_TYPE_SVG); // Preferred support
However, I noticed there is support for adding SVG images to XSLF.
Is there any way of adding an SVG image to a XSSFWorkbook?
Support for SVG is a new feature of Microsoft Office 365. That's why it is not fully supported by apache poi until now (May 2021, apache poi 5.0.0).
To implement that support in Excel one needs to know how Excel inserts SVG images. It converts the images to PNG for backwards compatibility. And it puts both, the SVG image as well as the PNG image, into the workbook. Then it shows the PNG image as a shape in the drawing. That shape has an additional reference to the SVG image, so if Excel 365 is used, the SVG image can be got too.
To implement that support in apache poi following is needed:
A SVG to PNG converter. There Apache Batik Transcoder can be used.
An extended XSSFWorkbook which provides either a separate addSVGPicture method or provides support for SVG in addPicture method. But extending XSSF... classes is not as simple as it could be, because of some weird decisions to make members or methods private.
A XSSFRelation.IMAGE_SVG to provide creating relations while creating pictures and shapes. But XSSFRelation is not extendable at all. So extending the low level POIXMLRelation is needed.
An extended XSSFPictureData to provide support for needed picture constructors for the new POIXMLRelation.
Following code provides all that. It is commented where needed.
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
class CreateExcelXSSFPictureSVG {
// use org.apache.batik.transcoder to convert SVG to PNG
static byte[] svgToPng(InputStream svg) throws Exception {
org.apache.batik.transcoder.image.PNGTranscoder t = new org.apache.batik.transcoder.image.PNGTranscoder();
org.apache.batik.transcoder.TranscoderInput input = new org.apache.batik.transcoder.TranscoderInput(svg);
java.io.ByteArrayOutputStream ostream = new java.io.ByteArrayOutputStream();
org.apache.batik.transcoder.TranscoderOutput output = new org.apache.batik.transcoder.TranscoderOutput(ostream);
t.transcode(input, output);
ostream.flush();
return ostream.toByteArray();
}
public static void main(String[] args) throws Exception {
String svgFilePath = "./Freesample.svg";
MyXSSFWorkbook workbook = new MyXSSFWorkbook(); // see MyXSSFWorkbook.java
// add SVG Image to workbook
FileInputStream is = new FileInputStream(svgFilePath);
int svgPictureIdx = workbook.addSVGPicture(is);
is.close();
// add PNG image to workbook
is = new FileInputStream(svgFilePath);
int pngPictureIdx = workbook.addPicture(svgToPng(is), Workbook.PICTURE_TYPE_PNG);
is.close();
// create sheet and get drawing
XSSFSheet sheet = workbook.createSheet("Sheet1");
XSSFDrawing drawing = sheet.createDrawingPatriarch();
// add SVG Image relation to drawing
XSSFPictureData pictureData = workbook.getAllPictures().get(svgPictureIdx);
org.apache.poi.ooxml.POIXMLDocumentPart.RelationPart rp = drawing.addRelation(null, XSSFRelation.IMAGES, pictureData);
String svgRId = rp.getRelationship().getId();
// create anchor for picture shape
XSSFCreationHelper helper = workbook.getCreationHelper();
XSSFClientAnchor anchor = helper.createClientAnchor();
anchor.setCol1(1);
anchor.setRow1(1);
// create PNG picture shape
XSSFPicture pngPicture = drawing.createPicture(anchor, pngPictureIdx);
pngPicture.resize();
// set SVG extension to PNG picture shape
org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtension ext = pngPicture.getCTPicture().getBlipFill().getBlip().addNewExtLst().addNewExt();
ext.setUri("{96DAC541-7B7A-43D3-8B79-37D633B846F1}");
org.apache.xmlbeans.XmlCursor cursor = ext.newCursor();
cursor.toNextToken();
cursor.toNextToken();
cursor.beginElement(new javax.xml.namespace.QName("http://schemas.microsoft.com/office/drawing/2016/SVG/main", "svgBlip", "asvg"));
cursor.insertNamespace("asvg", "http://schemas.microsoft.com/office/drawing/2016/SVG/main");
cursor.insertAttributeWithValue(new javax.xml.namespace.QName("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "embed", "r"), svgRId);
cursor.dispose();
FileOutputStream out = new FileOutputStream("CreateExcelXSSFPictureSVG.xlsx");
workbook.write(out);
out.close();
workbook.close();
}
}
Used extended classes:
MyXSSFWorkbook.java
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFPictureData;
import org.apache.poi.xssf.usermodel.XSSFFactory;
import org.apache.poi.util.IOUtils;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.lang.reflect.Field;
public class MyXSSFWorkbook extends XSSFWorkbook {
public int addSVGPicture(InputStream is) throws Exception {
Field _xssfFactory = XSSFWorkbook.class.getDeclaredField("xssfFactory");
_xssfFactory.setAccessible(true);
XSSFFactory xssfFactory = (XSSFFactory)_xssfFactory.get(this);
int imageNumber = getAllPictures().size() + 1;
Field _pictures = XSSFWorkbook.class.getDeclaredField("pictures");
_pictures.setAccessible(true);
#SuppressWarnings("unchecked")
List<XSSFPictureData> pictures = (List<XSSFPictureData>)_pictures.get(this);
MyXSSFPictureData img = createRelationship(MyXSSFRelation.IMAGE_SVG, xssfFactory, imageNumber, true).getDocumentPart(); // see MyXSSFPictureData.java and MyXSSFRelation.java
try (OutputStream out = img.getPackagePart().getOutputStream()) {
IOUtils.copy(is, out);
}
pictures.add(img);
return imageNumber - 1;
}
}
MyXSSFRelation.java
import org.apache.poi.ooxml.POIXMLRelation;
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
public final class MyXSSFRelation extends POIXMLRelation {
public static final MyXSSFRelation IMAGE_SVG = new MyXSSFRelation(
"image/svg",
PackageRelationshipTypes.IMAGE_PART,
"/xl/media/image#.svg",
MyXSSFPictureData::new, MyXSSFPictureData::new // see MyXSSFPictureData.java
);
private MyXSSFRelation(String type, String rel, String defaultName,
NoArgConstructor noArgConstructor,
PackagePartConstructor packagePartConstructor) {
super(type, rel, defaultName, noArgConstructor, packagePartConstructor, null);
}
}
MyXSSFPictureData.java
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.xssf.usermodel.XSSFPictureData;
public class MyXSSFPictureData extends XSSFPictureData {
protected MyXSSFPictureData() {
super();
}
protected MyXSSFPictureData(PackagePart part) {
super(part);
}
}

How to set font family in iText 5?

I can't figure out how to simply set a font family on an existing pdf field with iText 5. I've found the official documentation on this issue to be pretty lacking.
Here is how I'm trying to call this font (I just want Helvetica, which comes w/ the FontFactory library):
Font Helvetica = FontFactory.getFont("Helvetica");
form.setFieldProperty("Text Field 1", "textfont", "Helvetica", null);
When I run the above code, I get the console error message:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to com.itextpdf.text.pdf.BaseFont
at com.itextpdf.text.pdf.AcroFields.setFieldProperty(AcroFields.java:1033)
at com.eb.anico.web.pdf.manipulatePdf(pdf.java:87)
at com.eb.anico.web.pdf.main(pdf.java:55)
I'm able to do other things like make a field multi-line, center the field, and change font color. But changing font family still wont work. Here's my full code:
package com.pathtopackage;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.Font.FontFamily;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.AcroFields.Item;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.FontSelector;
import com.itextpdf.text.pdf.GrayColor;
import com.itextpdf.text.pdf.PdfAction;
import com.itextpdf.text.pdf.PdfArray;
import com.itextpdf.text.pdf.PdfDictionary;
import com.itextpdf.text.pdf.PdfFormField;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfNumber;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.PushbuttonField;
import com.itextpdf.text.pdf.TextField;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class pdf {
public static final String SRC = "src/resources/ads-horizontal-box-defined-size-single-line.pdf";
public static final String DEST = "src/resources/ads-horizontal-box-defined-size-single-line-STYLED-TXTSIZEAUTOACROBAT-7.pdf";
public static void main(String[] args) throws DocumentException, IOException {
File file = new File(DEST);
file.getParentFile().mkdirs();
new pdf().manipulatePdf(SRC, DEST);
}
public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
AcroFields form = stamper.getAcroFields();
Item item = form.getFieldItem("Text Field 1");
form.setFieldProperty("Text Field 1", "fflags", PdfFormField.FF_MULTILINE, null); //convert field to multiline
item.getMerged(0).put(PdfName.Q, new PdfNumber(PdfFormField.Q_CENTER)); //center form field
Font Helvetica = FontFactory.getFont("Helvetica");
form.setFieldProperty("Text Field 1", "textfont", "Helvetica", null);
form.setFieldProperty("Text Field 1", "textcolor", new BaseColor(0, 93, 171), null);
form.setFieldProperty("Text Field 1", "textsize", new Float(16), null); //change field font size to 16
PdfDictionary widget = item.getWidget(0);
PdfArray rect = widget.getAsArray(PdfName.RECT);
rect.set(2, new PdfNumber(rect.getAsNumber(2).floatValue()));
String value = "Kyle Vassella";
form.setField("Text Field 1", value);
stamper.setFormFlattening(true); //flatten pdf
stamper.close();
}
}
All of this is neatly explained in ´iText in action´ chapter 8.
https://developers.itextpdf.com/examples/itext-action-second-edition/chapter-8
One way of going about this, is to use the individual form objects themselves, rather than the form as a whole.
PushbuttonField button = new PushbuttonField(writer, rect, "Buttons");
button.setBackgroundColor(new GrayColor(0.75f));
button.setBorderColor(GrayColor.GRAYBLACK);
button.setBorderWidth(1);
button.setBorderStyle(PdfBorderDictionary.STYLE_BEVELED);
button.setTextColor(GrayColor.GRAYBLACK);
button.setFontSize(12);
Check out the API for iText5 as well, so that you have a clear idea of what the parameters for each method are.
http://itextsupport.com/apidocs/iText5/5.5.11/

add a annotation to a PDF with PDFBOX then overlay another PDF on line 2

I have to create a one line annotation on top of an existing PDF with PDFBOX. It has to be in the upper right hand corner of the existing PDF. Most of the time, there is space there to append the line in the existing PDF. Sometimes there is a stamp in the right hand corner that prohibits me from putting the annotation there. The best I have been able to do is write a new page with the annotation on top, then merge the 2 PDFs together. Annotation code:
import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
public class Document_Creation {
public static void main (String args[]) throws IOException {
//Creating PDF document object
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
//Retrieving the pages of the document
PDPageContentStream contentStream = new PDPageContentStream(document, page,PDPageContentStream.AppendMode.APPEND,true,true);
//Begin the Content stream
contentStream.beginText();
//Setting the font to the Content stream
contentStream.setFont(PDType1Font.TIMES_BOLD, 8);
//Setting the position for the line
contentStream.newLineAtOffset(100, 775);
String text = args[1];
//Adding text in the form of string
contentStream.showText(text);
//Ending the content stream
contentStream.endText();
System.out.println("Content added best friend");
//Closing the content stream
contentStream.close();
//Saving the document
document.save(args[0]);
//Closing the document
document.close();

Apache poi slide show to pdf conversion

Is there any way to convert generated .ppt file using apache poi to .pdf file?
OR
Any way to convert PPT file to PDF file using JAVA?
Gagravarr, thanks you for your comment with following approach: PPT -> images -> PDF. It gave me clues for further solutions.
Recently, I've faced the same task: convert PPT reports into PDF reports using Java facilities. PPT reports are generated via Apache POI lib, and I intended to reuse ready created PPT structure.
I developed two solutions, each has its own advantages/disadvantages. And both of them using iText library with version 2.1.7.(which is free to use, and which is great)). Both of them do support Japanese symbols after additional enhancement.
1. Apache POI Slide -> image -> PDF
Demonstration code example:
package com.test.pdf;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.hslf.model.Slide;
import org.apache.poi.hslf.usermodel.SlideShow;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Image;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfWriter;
public class PPTtoImageToPDFexample {
public static void main(String[] args) throws IOException, DocumentException {
//load any ppt file
FileInputStream inputStream = new FileInputStream("d:/temp/initialPPT.ppt");
SlideShow ppt = new SlideShow(inputStream);
inputStream.close();
Dimension pgsize = ppt.getPageSize();
//take first slide and save it as an image
Slide slide = ppt.getSlides()[0];
BufferedImage img = new BufferedImage(pgsize.width, pgsize.height,
BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
graphics.setPaint(Color.white);
graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width,
pgsize.height));
slide.draw(graphics);
FileOutputStream out = new FileOutputStream("d:/temp/slideImage.png");
javax.imageio.ImageIO.write(img, "png", out);
out.close();
//get saved slide-image and save it into pdf
Image slideImage = Image.getInstance("d:/temp/slideImage.png");
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream("d:/temp/PPTtoImageTest.pdf"));
document.setPageSize(new Rectangle(slideImage.getWidth(), slideImage.getHeight()));
document.open();
slideImage.setAbsolutePosition(0, 0);
document.add(slideImage);
document.close();
}
}
2. This approach works on-fly: take Apache POI Slide -> get awt.Graphics2 instance from it -> pass this interface to the iText
draw engine.
Demonstration code example:
package com.test.pdf;
import java.awt.Dimension;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.hslf.model.Slide;
import org.apache.poi.hslf.usermodel.SlideShow;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfGraphics2D;
import com.lowagie.text.pdf.PdfWriter;
public class PPTtoPDFdirectly {
public static void main(String[] args) throws IOException, DocumentException {
//load any ppt file
FileInputStream inputStream = new FileInputStream("d:/temp/initialPPT.ppt");
SlideShow ppt = new SlideShow(inputStream);
inputStream.close();
Dimension pgsize = ppt.getPageSize();
//take first slide and draw it directly into PDF via awt.Graphics2D interface.
Slide slide = ppt.getSlides()[0];
Document document = new Document();
PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream("d:/temp/PPTtoPDF.pdf"));
document.setPageSize(new Rectangle((float)pgsize.getWidth(), (float) pgsize.getHeight()));
document.open();
PdfGraphics2D graphics = (PdfGraphics2D) pdfWriter.getDirectContent().createGraphics((float)pgsize.getWidth(), (float)pgsize.getHeight());
slide.draw(graphics);
graphics.dispose();
document.close();
}
}
One option is to use POI to convert each slide into an image, then use something like Apache PDFBox to place each image onto it's own PDF page. This should work well for simpler PPT files, but the code to render slides is still a WIP. So, if you have a very complex slide, you may find some bits missing/incorrect, do send in patches if you fix any of these gaps!
Otherwise, your other option is to use the Java bindings for OpenOffice, and have that do the conversion for you

Categories