ODFDOM Tutorial Index > Creating Presentation Documents

Creating Presentation Documents Using ODFDOM

The input for the program in this tutorial is a text file with these contents:

// Each line contains: // slide title, country name, city name, image path Rathaus,Germany,Hamburg,../src/images/rathaus.jpg Namdaemun (남대문),South Korea,Seoul (서을),../src/images/namdaemun.jpg

The program’s output will be an OpenDocument presentation file. Here is a slightly edited screenshot of what the first slide will look like.

The area at the left that looks like a table is not an OdfTable like the one in the previous tutorial. Instead, it is just a set of four rectangles that have text labels.

The program is written to be run from the command line program; you invoke it with a command like this:

java -jar simple_odp.jar inputfile.txt outputfile.ods

The Structure of a Presentation Document

In order for the following code to make sense, you need to know how a presentation document is represented in OpenDocument. An ODF presentation’s content.xml file contains an <office:presentation> element. Each of its <draw:page> children represents one slide. within the document.

Geometric shapes such as rectangles and circles are drawn directly on to the page. Text and images are placed inside a “frame” that contains the position and dimensions of that particular object; in ODFDOM, these are represented by the OdfDrawFrame class.

Input Variables

The name of the input file will come straight from args[0] rather than be stored in a separate variable. In order to make it easier to manipulate the input file, the program will create an object of an auxiliary class SlideInfo for each line in the file. This class has the following members and constructors:

class SlideInfo { String country; String city; String imagePath; String title; public SlideInfo(String title, String country, String city, String imagePath) { this.title = title.trim(); this.country = country.trim(); this.city = city.trim(); this.imagePath = imagePath.trim(); } public SlideInfo(String[] info) { this(info[0], info[1], info[2], info[3]); } // getter/setter methods for members omitted to save space. }

The main class declares an ArrayList to hold the input data:

ArrayList<SlideInfo> slideList;

Output Variables

In this program, the name of the output file will also come straight from args[1] rather than being stored in a separate variable. However, as in the previous tutorials, we do need variables for the presentation document and its “access points.”

OdfPresentationDocument outputDocument; OdfOfficePresentation officePresentation; OdfFileDom contentDom; // the document object model for content.xml OdfFileDom stylesDom; // the document object model for styles.xml OdfOfficeAutomaticStyles contentAutoStyles;

The presentation needs to create automatic styles in the content.xml only, so we will dispense with variables for accessing the styles.xml named and automatic styles.

Once again, because the ODFDOM toolkit automatically generates unique style names for you, the program must keep track of their names in order to apply those styles to elements.

String slideTitleStyleName; String imageStyleName; String fauxTableRectangleStyleName; String fauxTableTextStyleName;

These styles are used for the slide title, the image, the rectangles that make up the fake (faux) table and the text inside those rectangles.

This having been done, here is the main() method, which creates an application and runs it via the run() method:

public static void main(String[] args) { SimpleOdp app = new SimpleOdp(); if (args.length == 2) { app.run(args); } else { System.err.println("Usage: java -jar simple_odp.jar infile outfile"); } } void run(String[] args) { slideList = new ArrayList<SlideInfo>(); processInput(args[0]); setupOutputDocument(); if (slideList != null && outputDocument != null) { setupOutputDocument(); cleanOutDocument(); newStyles(); createPages(); saveOutputDocument(args[1]); } }

Processing the Input Document

This function parses the input file. It does not use any calls to ODFDOM, but it is given here for the sake of completeness. If the parsing goes well, the slideList is filled; otherwise it is set to null.

private void processInput(String inputFileName) { BufferedReader dataFile; String data; try { dataFile = new BufferedReader(new FileReader(inputFileName)); data = dataFile.readLine(); while (data != null) { if (!(data.startsWith("//") || data.matches("^\\s*$"))) { slideList.add(new SlideInfo(data.split("\\s*,\\s*"))); } data = dataFile.readLine(); } dataFile.close(); } catch (FileNotFoundException e) { System.err.println("Unable to open file" + inputFileName); slideList = null; } catch (IOException e) { System.err.println("Error reading file " + inputFileName); slideList = null; } }

Creating the Output Document

The setupOutputDocument() method starts by calling newPresentationDocument() to create an ODF presentation document from a template that is built into the library. Once you have the document, the method gets the the Document Object Model (a subclass of Document) for the content.xml and the <office:presentation> element that is the content root. All of the data that make up the document’s content will be children of this element.

void setupOutputDocument() { try { outputDocument = OdfSpreadsheetDocument.newSpreadsheetDocument(); contentDom = outputDocument.getContentDom(); contentAutoStyles = contentDom.getOrCreateAutomaticStyles(); officePresentation = outputDocument.getContentRoot(); } catch (Exception e) { System.err.println("Unable to create output file."); System.err.println(e.getMessage()); outputDocument = null; } }

Clearing Content from the Output Document

The templates included in the ODFDOM toolkit have content in them; a newly-created presentation document contains an empty page. The cleanOutDocument() method gets rid of any existing pages, and is built along the same lines as described in the first tutorial, so we won’t duplicate the code here.

Adding Styles

Here is the code for adding the four styles that this program needs. The OdfStyleGraphicProperties.Clip property is a rectangle that tells how much of the image to clip. The values are for the top, right, bottom, and left side of the image (clockwise from 12:00). In this case, you want the entire picture, so all four values are set to zero.

void addAutomaticStyles() { OdfStyle style; /* The style for the slide title */ style = contentAutoStyles.newStyle(OdfStyleFamily.Presentation); slideTitleStyleName = style.getStyleNameAttribute(); style.setProperty(OdfStyleGraphicProperties.AutoGrowHeight, "true"); style.setProperty(OdfStyleGraphicProperties.MinHeight, "3cm"); style.setProperty(OdfStyleTextProperties.FontFamily, "Helvetica"); style.setProperty(OdfStyleTextProperties.FontSize, "44pt"); /* Style for the image element */ style = contentAutoStyles.newStyle(OdfStyleFamily.Graphic); imageStyleName = style.getStyleNameAttribute(); style.setProperty(OdfStyleGraphicProperties.MinHeight, "10cm"); style.setProperty(OdfStyleGraphicProperties.Clip, "rect(0cm, 0cm, 0cm, 0cm)"); /* Graphic style for the table rectangles */ style = contentAutoStyles.newStyle(OdfStyleFamily.Graphic); fauxTableRectangleStyleName = style.getStyleNameAttribute(); style.setProperty(OdfStyleGraphicProperties.FillColor, "#ffffff"); style.setProperty(OdfStyleGraphicProperties.TextareaVerticalAlign, "middle"); style.setProperty(OdfStyleGraphicProperties.PaddingLeft, "8pt"); /* Style for the text in the table */ style = contentAutoStyles.newStyle(OdfStyleFamily.Paragraph); fauxTableTextStyleName = style.getStyleNameAttribute(); style.setProperty(OdfStyleTextProperties.FontFamily, "Helvetica"); style.setProperty(OdfStyleTextProperties.FontSize, "16pt"); }

Creating the Slides

The beginning of the createPages() method establishes the variables. The counter variable lets you give consecutive style names to each of the slides.

void createPages() { OdfDrawPage page; OdfDrawFrame frame; OdfDrawImage image; OdfDrawTextBox textBox; OdfTextParagraph para; int counter = 0;

Now iterate through the slide information. The constructor for a new OdfDrawPage requires the name of a “master page,” but you may safely leave this as the empty string or null.

Since the title is text rather than a graphic primitive such as a line, it must be placed inside a OdfDrawFrame. The title’s position and dimensions are set in the frame as the the x, y, width and height. In the XML, these attributes are borrowed from the SVG (Scalable Vector Graphics) namespace, hence the Svg prefix.

/* Each slide goes on its own page */ for (SlideInfo info : slideList) { page = (OdfDrawPage) officePresentation.newDrawPageElement(""); page.setDrawNameAttribute("pg" + Integer.toString(++counter)); /* Create the title in a <draw:frame> element */ frame = (OdfDrawFrame) page.newDrawFrameElement(); frame.setPresentationStyleNameAttribute(slideTitleStyleName); frame.setPresentationClassAttribute( PresentationClassAttribute.Value.TITLE.toString()); frame.setSvgXAttribute("1.4cm"); frame.setSvgYAttribute("0.85cm"); frame.setSvgWidthAttribute("25cm"); frame.setSvgHeightAttribute("3.5cm"); /* Which contains a <draw:text-box> that has a <text:p> inside it */ textBox = (OdfDrawTextBox) frame.newDrawTextBoxElement(); para = (OdfTextParagraph) textBox.newTextPElement(); para.addContent(info.getTitle());

Next, the page needs the image. It also is inside a frame. Inserting the image with the insertImage method does all the work for you; it brings the file into the Pictures directory of the ODF document structure. If the image can’t be found or there’s some other type of error, the program does nothing rather than throw an exception.

Because we want all the images to have the same width (12cm), we must read the iamge again with ImageIO.read and calculate the correct height for the image.

try { frame = (OdfDrawFrame) page.newDrawFrameElement(); frame.setPresentationStyleNameAttribute(imageStyleName); frame.setPresentationClassAttribute( PresentationClassAttribute.Value.GRAPHIC.toString()); image = (OdfDrawImage) frame.newDrawImageElement(); image.insertImage(new URI(info.getImagePath())); BufferedImage buffer = ImageIO.read(new File( info.getImagePath())); frame.setSvgXAttribute("14cm"); frame.setSvgYAttribute("6cm"); /* * Always make the picture 12cm wide, and adjust * the height in proportion to the width */ frame.setSvgWidthAttribute("12cm"); frame.setSvgHeightAttribute(Double.toString( (12.0 * buffer.getHeight()) / buffer.getWidth()) + "cm"); /* Put in an empty paragraph--not necessary, but doesn't hurt anything */ para = (OdfTextParagraph) image.newTextPElement(); } catch (Exception e) { // do nothing; no picture will be added. }

Finally, each page has to have the rectangles with the country and city names. This task is delegated to a separate method:

page.appendChild(createDrawnTable(info)); } }

Labelled Rectangles

The code to create the “table” creates an OdfDrawGroup of the four rectangles and returns that group to be inserted into the page. If you open the resulting file in OpenOffice.org and click the table, you will be able to ungroup it into its component rectangles.

The createDrawnTable in turn calls the helper method createTextRectangle, whose parameters are as described in the javadoc.

private OdfDrawGroup createDrawnTable(SlideInfo info) { OdfDrawGroup group = new OdfDrawGroup(contentDom); group.appendChild(createTextRectangle( "Country", "1.5cm", "6cm", "4.7cm", "1cm")); group.appendChild(createTextRectangle( info.getCountry(), "6.2cm", "6cm", "7cm", "1cm")); group.appendChild(createTextRectangle( "City", "1.5cm", "7cm", "4.7cm", "1cm")); group.appendChild(createTextRectangle( info.getCity(), "6.2cm", "7cm", "7cm", "1cm")); return group; } /* * Create a rectangle that contains text. * @param textValue the text to display inside the rectangle * @param x coordinate of left corner of rectangle * @param y coordinate of upper corner of rectangle * @param width width of rectangle * @param height height of rectangle */ private OdfDrawRect createTextRectangle(String textValue, String x, String y, String width, String height) { OdfDrawRect r = new OdfDrawRect(contentDom); OdfTextParagraph p; r.setSvgXAttribute(x); r.setSvgYAttribute(y); r.setSvgWidthAttribute(width); r.setSvgHeightAttribute(height); r.setDrawStyleNameAttribute(fauxTableRectangleStyleName); p = new OdfTextParagraph(contentDom); p.setStyleName(fauxTableTextStyleName); p.appendChild(contentDom.createTextNode(textValue)); r.appendChild(p); return r; }

Saving the Output Document

This is the easiest part of the program: only one line of actual code, surrounded by error handling.

void saveOutputDocument() { try { outputDocument.save(outputFileName); } catch (Exception e) { System.err.println("Unable to save document."); System.err.println(e.getMessage()); } }

The Entire Program

You may download the src directory for this program. This directory comes from a NetBeans project.