How to add SVG image to XSSFWorkBook - java

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);
}
}

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 reduce size of merged pdf and speed up merging operation with PDFBox?

We are using PDFBox 2.0.17 (main reason:free) with java 8 to merge two types of PDF documents (Normal PDF/A and converted PDF from Tiff file).
We found that the size of resulting PDF file is quite big - basically the total size of the all PDFs. I am trying to find a way to reduce the resulting file size.
I found a stackoverflow link How to reduce the size of merged PDF/A-1b files with pdfbox or other java library. But it did not seem to help.
Is there any way to reduce the size of resulting PDF through the following ?
Font optimization, Image optimization & compression PDF compression
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
public class MergerTest {
public static void main(String[] args) throws IOException {
File file1 = new File("C:\\Test\\PdfBox_Examples\\doc1.pdf");
File file2 = new File("C:\\Test\\PdfBox_Examples\\doc2.pdf");
//Instantiating PDFMergerUtility class
PDFMergerUtility PDFmerger = new PDFMergerUtility();
//Setting the destination file
PDFmerger.setDestinationFileName("C:\\Test\\PdfBox_Examples\\merged.pdf");
//adding the source files
PDFmerger.addSource(file1);
PDFmerger.addSource(file2);
//Merging the two documents
PDFmerger.mergeDocuments(null);
System.out.println("Documents merged");
File file = new File("C:\\Test\\PdfBox_Examples\\merged.pdf");
PDDocument doc = PDDocument.load(file);
Map<String, COSBase> fontFileCache = new HashMap<>();
for (int pageNumber = 0; pageNumber < doc.getNumberOfPages();
pageNumber++) {
final PDPage page = doc.getPage(pageNumber);
COSDictionary pageDictionary = (COSDictionary)
page.getResources().getCOSObject().getDictionaryObject
(COSName.FONT);
if(pageDictionary !=null) {
for (COSName currentFont : pageDictionary.keySet()) {
COSDictionary fontDictionary = (COSDictionary)
pageDictionary.getDictionaryObject(currentFont);
for (COSName actualFont : fontDictionary.keySet()) {
COSBase actualFontDictionaryObject =
fontDictionary.getDictionaryObject(actualFont);
if (actualFontDictionaryObject instanceof COSDictionary)
{
COSDictionary fontFile = (COSDictionary)
actualFontDictionaryObject;
if (fontFile.getItem(COSName.FONT_NAME) instanceof
COSName) {
COSName fontName = (COSName)
fontFile.getItem(COSName.FONT_NAME);
fontFileCache.computeIfAbsent(fontName.getName(), key ->
fontFile.getItem(COSName.FONT_FILE2));
fontFile.setItem(COSName.FONT_FILE2,
fontFileCache.get(fontName.getName()));
}
}
}
}
}else {
System.out.println("pageDictionary is null - likely Converted PDF
from Tiff");
}
}
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
doc.save(baos);
final File compressed = new
File("C:\\Test\\PdfBox_Examples\\test_compressed.pdf");
baos.writeTo(new FileOutputStream(compressed));
System.out.println("Documents compressed");
}
}
//Note: I have also tested using tiff_1.pdf and tiff_2.pdf as the inputs.

How to set a gradient fill for an XSLF shape created in an Apache POI presentations?

I need to create a rectangle with a horizontal or vertical gradient fill in a PowerPoint presentation I'm creating with the Apache POI API.
I'm using XSLFFreeformShape for the rectangle:
XSLFGroupShape currentGroup = ...;
XSLFFreeformShape ppShape = currentGroup.createFreeform();
ppShape.setPath( ... );
Setting a single, flat color is easy:
ppShape.setFillColor( myColor );
And there's a mechanism to get details of the gradient fill, via superclass method XSLFSimpleShape.getFillStyle().
But there's no corresponding setFillStyle(), and I don't see any other easy public way to specify a gradient.
Question: Can I create a rectangle with a gradient, and if so, how?
When defining the current API, I was mainly focused on giving the image renderer enough information and left out probably a lot of write access properties.
So currently you need to use the XmlObjects to set the gradient:
package org.apache.poi.xslf;
import java.awt.geom.Rectangle2D;
import java.io.FileOutputStream;
import org.apache.poi.sl.usermodel.ShapeType;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFAutoShape;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.junit.Test;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientFillProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientStop;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientStopList;
import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
public class TestGradientFill {
#Test
public void fill() throws Exception {
XMLSlideShow ppt = new XMLSlideShow();
XSLFSlide sl = ppt.createSlide();
XSLFAutoShape as = sl.createAutoShape();
as.setShapeType(ShapeType.STAR_12);
as.setAnchor(new Rectangle2D.Double(100, 100, 100, 100));
CTShape cs = (CTShape)as.getXmlObject();
CTGradientFillProperties gFill = cs.getSpPr().addNewGradFill();
gFill.addNewLin().setAng(1800000);
CTGradientStopList list = gFill.addNewGsLst();
// set the start pos
CTGradientStop stop = list.addNewGs();
stop.setPos(0);
stop.addNewSrgbClr().setVal(new byte[]{ (byte)0x94, (byte)0xbd, (byte)0x5e });
// set the end pos (100000 = 100%)
stop = list.addNewGs();
stop.setPos(100000);
stop.addNewSrgbClr().setVal(new byte[]{ 0,0,0 });
FileOutputStream fos = new FileOutputStream("gradient.pptx");
ppt.write(fos);
fos.close();
}
}

Adding PDFStamper overlay causes XFA pre-populated fields to disappear

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?

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