]> source.dussan.org Git - poi.git/commitdiff
Ported to SS model.
authorMark Beardsley <msb@apache.org>
Fri, 12 Mar 2010 11:23:44 +0000 (11:23 +0000)
committerMark Beardsley <msb@apache.org>
Fri, 12 Mar 2010 11:23:44 +0000 (11:23 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@922219 13f79535-47bb-0310-9956-ffa450edef68

src/examples/src/org/apache/poi/ss/examples/AddDimensionedImage.java [new file with mode: 0644]

diff --git a/src/examples/src/org/apache/poi/ss/examples/AddDimensionedImage.java b/src/examples/src/org/apache/poi/ss/examples/AddDimensionedImage.java
new file mode 100644 (file)
index 0000000..b7dc583
--- /dev/null
@@ -0,0 +1,976 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+\r
+package org.apache.poi.ss.examples;\r
+\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.net.URL;\r
+\r
+import org.apache.commons.io.FilenameUtils;\r
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;\r
+import org.apache.poi.ss.usermodel.ClientAnchor;\r
+import org.apache.poi.ss.usermodel.Drawing;\r
+import org.apache.poi.ss.usermodel.Row;\r
+import org.apache.poi.ss.usermodel.Sheet;\r
+import org.apache.poi.ss.usermodel.Workbook;\r
+import org.apache.poi.ss.util.CellReference;\r
+import org.apache.poi.util.IOUtils;\r
+\r
+\r
+/**\r
+ * Demonstrates how to add an image to a worksheet and set that image's size\r
+ * to a specific number of milimetres irrespective of the width of the columns\r
+ * or height of the rows. Overridden methods are provided so that the location\r
+ * of the image - the cells row and column co-ordinates that define the top\r
+ * left hand corners of the image - can be identified either in the familiar\r
+ * Excel manner - A1 for instance - or using POI's methodolody of a column and\r
+ * row index where 0, 0 would indicate cell A1.\r
+ *\r
+ * The best way to make use of these techniques is to delay adding the image to\r
+ * the sheet until all other work has been completed. That way, the sizes of\r
+ * all rows and columns will have been adjusted - assuming that step was\r
+ * necessary. Even though the anchors type is set to prevent the image moving\r
+ * or re-sizing, this setting does not have any effect until the sheet is being\r
+ * viewed using the Excel application.\r
+ *\r
+ * The key to the process is the ClientAnchor class. It defines methods that allow\r
+ * us to define the location of an image by specifying the following;\r
+ *\r
+ *      * How far - in terms of co-ordinate positions - the image should be inset\r
+ *      from the left hand border of a cell.\r
+ *      * How far - in terms of co-ordinate positions - the image should be inset\r
+ *      from the from the top of the cell.\r
+ *      * How far - in terms of co-ordinate positions - the right hand edge of\r
+ *      the image should protrude into a cell (measured from the cell's left hand\r
+ *      edge to the image's right hand edge).\r
+ *      * How far - in terms of co-ordinate positions - the bottm edge of the\r
+ *      image should protrude into a row (measured from the cell's top edge to\r
+ *      the image's bottom edge).\r
+ *      * The index of the column that contains the cell whose top left hand\r
+ *      corner should be aligned with the top left hand corner of the image.\r
+ *      * The index of the row that contains the cell whose top left hand corner\r
+ *      should be aligned with the image's top left hand corner.\r
+ *      * The index of the column that contains the cell whose top left hand\r
+ *      corner should be aligned with the image's bottom right hand corner\r
+ *      * The index number of the row that contains the cell whose top left\r
+ *      hand corner should be aligned with the images bottom right hand corner.\r
+ *\r
+ * It can be used to add an image into cell A1, for example, in the following\r
+ * manner;\r
+ *\r
+ *      ClientAnchor anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor();\r
+ *\r
+ *      anchor.setDx1(0);\r
+ *      anchor.setDy1(0);\r
+ *      anchor.setDx2(0);\r
+ *      anchor.setDy2(0);\r
+ *      anchor.setCol1(0);\r
+ *      anchor.setRow1(0);\r
+ *      anchor.setCol2(1);\r
+ *      anchor.setRow2(1);\r
+ *\r
+ * Taken together, the first four methods define the locations of the top left\r
+ * and bottom right hand corners of the image if you imagine that the image is\r
+ * represented by a simple rectangle. The setDx1() and setDy1() methods locate\r
+ * the top left hand corner of the image while setDx2() and and Dy2() locate the\r
+ * bottom right hand corner of the image. An individual image can be inserted\r
+ * into a single cell or is can lie across many cells and the latter four methods\r
+ * are used to define just where the image should be positioned. They do this by\r
+ * again by identifying where the top left and bottom right hand corners of the\r
+ * image should be located but this time in terms of the indexes of the cells\r
+ * in which those corners should be located. The setCol1() and setRow1() methods\r
+ * together identify the cell that should contain the top left hand corner of\r
+ * the image while setCol2() and setRow2() do the same for the images bottom\r
+ * right hand corner. \r
+ *\r
+ * Knowing that, it is possible to look again at the example above and to see\r
+ * that the top left hand corner of the image will be located in cell A1 (0, 0)\r
+ * and it will be aligned with the very top left hand corner of the cell. Likewise,\r
+ * the bottom right hand corner of the image will be located in cell B2 (1, 1) and\r
+ * it will again be aligned with the top left hand corner of the cell. This has the\r
+ * effect of making the image seem to occupy the whole of cell A1. Interestingly, it\r
+ * also has an effect on the image's resizing behaviour because testing has \r
+ * demonstrated that if the image is wholly contained within one cell and is not\r
+ * 'attached' for want of a better word, to a neighbouring cell, then that image\r
+ * will not increase in size in response to the user dragging the column wider\r
+ * or the cell higher.\r
+ *\r
+ * The following example demonstrates a slightly different way to insert an\r
+ * image into cell A1 and to ensure that it occupies the whole of the cell. This\r
+ * is accomplised by specifying the the images bottom right hand corner should be\r
+ * aligned with the bottom right hand corner of the cell. It is also a case\r
+ * where the image will not increase in size if the user increases the size of\r
+ * the enclosing cell - irrespective of the anchor's type - but it will reduce in\r
+ * size if the cell is made smaller.\r
+ *\r
+ *      ClientAnchor anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor();\r
+ *\r
+ *      anchor.setDx1(0);\r
+ *      anchor.setDy1(0);\r
+ *      anchor.setDx2(1023);\r
+ *      anchor.setDy2(255);\r
+ *      anchor.setCol1(0);\r
+ *      anchor.setRow1(0);\r
+ *      anchor.setCol2(0);\r
+ *      anchor.setRow2(0);\r
+ *\r
+ * Note that the final four method calls all pas the same value and seem to\r
+ * indicate that the images top left hand corner is aligned with the top left\r
+ * hand corner of cell A1 and that it's bottom right hand corner is also\r
+ * aligned with the top left hand corner of cell A1. Yet, running this code\r
+ * would see the image fully occupying cell A1. That is the result of the\r
+ * values passed to parameters three and four; these I have referred to as\r
+ * determing the images co-ordinates within the cell. They indicate that the\r
+ * image should occupy - in order - the full width of the column and the full\r
+ * height of the row.\r
+ *\r
+ * The co-ordinate values shown are the maxima; and they are independent of\r
+ * row height/column width and of the font used. Passing 255 will always result\r
+ * in the image occupying the full height of the row and passing 1023 will\r
+ * always result in the image occupying the full width of the column. They help\r
+ * in situations where an image is larger than a column/row and must overlap\r
+ * into the next column/row. Using them does mean, however, that it is often\r
+ * necessary to perform conversions between Excel's characters units, points,\r
+ * pixels and millimetres in order to establish how many rows/columns an image\r
+ * should occupy and just what the varous insets ought to be.\r
+ *\r
+ * Note that the setDx1(int) and setDy1(int) methods of the ClientAchor class\r
+ * are not made use of in the code that follows. It would be fairly trivial\r
+ * however to extend this example further and provide methods that would centre\r
+ * an image within a cell or allow the user to specify that a plain border a\r
+ * fixed number of millimetres wide should wrap around the image. Those first\r
+ * two parameters would make this sort of functionality perfectly possible.\r
+ *\r
+ * Owing to the various conversions used, the actual size of the image may vary\r
+ * from that required; testing has so far found this to be in the region of\r
+ * plus or minus two millimetres. Most likely by modifying the way the\r
+ * calculations are performed - possibly using double(s) throughout and\r
+ * rounding the values at the correct point - it is likely that these errors\r
+ * could be reduced or removed.\r
+ *\r
+ * A note concerning Excels' image resizing behaviour. The ClientAnchor\r
+ * class contains a method called setAnchorType(int) which can be used to\r
+ * determine how Excel will resize an image in reponse to the user increasing\r
+ * or decreasing the dimensions of the cell containing the image. There are \r
+ * three values that can be passed to this method; 0 = To move and size the \r
+ * image with the cell, 2 = To move but don't size the image with the cell,\r
+ * 3 = To prevent the image from moving or being resized along with the cell. If\r
+ * an image is inserted using this class and placed into a single cell then if\r
+ * the setAnchorType(int) method is called and a value of either 0 or 2 passed\r
+ * to it, the resultant resizing behaviour may be a surprise. The image will not\r
+ * grow in size of the column is made wider or the row higher but it will shrink\r
+ * if the columns width or rows height are reduced.\r
+ *\r
+ * @author Mark Beardsley [msb at apache.org] and Mark Southern [southern at scripps.edu]\r
+ * @version 1.00 5th August 2009.\r
+ *          2.00 26th February 2010.\r
+ *               Ported to make use of the the SS Usermodel classes.\r
+ *               Ability to reuse the Drawing Patriarch so that multiple images\r
+ *               can be inserted without unintentionally erasing earlier images.\r
+ *               Check on image type added; i.e. jpg, jpeg or png.\r
+ *               The String used to contain the files name is now converted\r
+ *               into a URL.\r
+ */\r
+public class AddDimensionedImage {\r
+\r
+    // Four constants that determine how - and indeed whether - the rows\r
+    // and columns an image may overlie should be expanded to accomodate that\r
+    // image.\r
+    // Passing EXPAND_ROW will result in the height of a row being increased\r
+    // to accomodate the image if it is not already larger. The image will\r
+    // be layed across one or more columns.\r
+    // Passing EXPAND_COLUMN will result in the width of the column being\r
+    // increased to accomodate the image if it is not already larger. The image\r
+    // will be layed across one or many rows.\r
+    // Passing EXPAND_ROW_AND_COLUMN will result in the height of the row\r
+    // bing increased along with the width of the column to accomdate the\r
+    // image if either is not already larger.\r
+    // Passing OVERLAY_ROW_AND_COLUMN will result in the image being layed\r
+    // over one or more rows and columns. No row or column will be resized,\r
+    // instead, code will determine how many rows and columns the image should\r
+    // overlie.\r
+    public static final int EXPAND_ROW = 1;\r
+    public static final int EXPAND_COLUMN = 2;\r
+    public static final int EXPAND_ROW_AND_COLUMN = 3;\r
+    public static final int OVERLAY_ROW_AND_COLUMN = 7;\r
+\r
+    /**\r
+     * Add an image to a worksheet.\r
+     *\r
+     * @param cellNumber A String that contains the location of the cell whose\r
+     *                   top left hand corner should be aligned with the top\r
+     *                   left hand corner of the image; for example "A1", "A2"\r
+     *                   etc. This is to support the familiar Excel syntax.\r
+     *                   Whilst images are are not actually inserted into cells\r
+     *                   this provides a convenient method of indicating where\r
+     *                   the image should be positioned on the sheet.\r
+     * @param sheet A reference to the sheet that contains the cell referenced\r
+     *              above.\r
+     * @param imageFile An instance of the URL class that encapsulates the name\r
+     *                  of and path to the image that is to be 'inserted into'\r
+     *                  the sheet.\r
+     * @param reqImageWidthMM A primitive double that contains the required\r
+     *                        width of the image in millimetres.\r
+     * @param reqImageHeightMM A primitive double that contains the required\r
+     *                         height of the image in millimetres.\r
+     * @param resizeBehaviour A primitive int whose value will determine how\r
+     *                        the code should react if the image is larger than\r
+     *                        the cell referenced by the cellNumber parameter.\r
+     *                        Four constants are provided to determine what\r
+     *                        should happen;\r
+     *                          AddDimensionedImage.EXPAND_ROW\r
+     *                          AddDimensionedImage.EXPAND_COLUMN\r
+     *                          AddDimensionedImage.EXPAND_ROW_AND_COLUMN\r
+     *                          AddDimensionedImage.OVERLAY_ROW_AND_COLUMN\r
+     * @throws java.io.FileNotFoundException If the file containing the image\r
+     *                                       cannot be located.\r
+     * @throws java.io.IOException If a problem occurs whilst reading the file\r
+     *                             of image data.\r
+     * @throws java.lang.IllegalArgumentException If an invalid value is passed\r
+     *                                            to the resizeBehaviour\r
+     *                                            parameter.\r
+     */\r
+    public void addImageToSheet(String cellNumber, Sheet sheet, Drawing drawing,\r
+            URL imageFile, double reqImageWidthMM, double reqImageHeightMM,\r
+            int resizeBehaviour) throws FileNotFoundException, IOException,\r
+                                                      IllegalArgumentException {\r
+        // Convert the String into column and row indices then chain the\r
+        // call to the overridden addImageToSheet() method.\r
+        CellReference cellRef = new CellReference(cellNumber);\r
+        this.addImageToSheet(cellRef.getCol(), cellRef.getRow(), sheet, drawing,\r
+                imageFile, reqImageWidthMM, reqImageHeightMM,resizeBehaviour);\r
+    }\r
+\r
+    /**\r
+     * Add an image to a worksheet.\r
+     *\r
+     * @param colNumber A primitive int that contains the index number of a\r
+     *                  column on the worksheet; POI column indices are zero\r
+     *                  based. Together with the rowNumber parameter's value,\r
+     *                  this parameter identifies a cell on the worksheet. The\r
+     *                  image's top left hand corner will be aligned with the\r
+     *                  top left hand corner of this cell.\r
+     * @param rowNumber A primtive int that contains the index number of a row\r
+     *                  on the worksheet; POI row indices are zero based.\r
+     *                  Together with the rowNumber parameter's value, this\r
+     *                  parameter identifies a cell on the worksheet. The\r
+     *                  image's top left hand corner will be aligned with the\r
+     *                  top left hand corner of this cell.\r
+     * @param sheet A reference to the sheet that contains the cell identified\r
+     *              by the two parameters above.\r
+     * @param imageFile An instance of the URL class that encapsulates the name\r
+     *                  of and path to the image that is to be 'inserted into'\r
+     *                  the sheet.\r
+     * @param reqImageWidthMM A primitive double that contains the required\r
+     *                        width of the image in millimetres.\r
+     * @param reqImageHeightMM A primitive double that contains the required\r
+     *                         height of the image in millimetres.\r
+     * @param resizeBehaviour A primitive int whose value will determine how\r
+     *                        the code should react if the image is larger than\r
+     *                        the cell referenced by the colNumber and\r
+     *                        rowNumber parameters. Four constants are provided\r
+     *                        to determine what should happen;\r
+     *                          AddDimensionedImage.EXPAND_ROW\r
+     *                          AddDimensionedImage.EXPAND_COLUMN\r
+     *                          AddDimensionedImage.EXPAND_ROW_AND_COLUMN\r
+     *                          AddDimensionedImage.OVERLAY_ROW_AND_COLUMN\r
+     * @throws java.io.FileNotFoundException If the file containing the image\r
+     *                                       cannot be located.\r
+     * @throws java.io.IOException If a problem occurs whilst reading the file\r
+     *                             of image data.\r
+     * @throws java.lang.IllegalArgumentException If an invalid value is passed\r
+     *                                            to the resizeBehaviour\r
+     *                                            parameter or if the extension\r
+     *                                            of the image file indicates that\r
+     *                                            it is of a type that cannot\r
+     *                                            currently be added to the worksheet.\r
+     */\r
+    public void addImageToSheet(int colNumber, int rowNumber, Sheet sheet, Drawing drawing,\r
+            URL imageFile, double reqImageWidthMM, double reqImageHeightMM,\r
+            int resizeBehaviour) throws FileNotFoundException, IOException,\r
+                                                     IllegalArgumentException {\r
+        ClientAnchor anchor = null;\r
+        ClientAnchorDetail rowClientAnchorDetail = null;\r
+        ClientAnchorDetail colClientAnchorDetail = null;\r
+        int imageType = 0;\r
+\r
+        // Validate the resizeBehaviour parameter.\r
+        if((resizeBehaviour != AddDimensionedImage.EXPAND_COLUMN) &&\r
+           (resizeBehaviour != AddDimensionedImage.EXPAND_ROW) &&\r
+           (resizeBehaviour != AddDimensionedImage.EXPAND_ROW_AND_COLUMN) &&\r
+           (resizeBehaviour != AddDimensionedImage.OVERLAY_ROW_AND_COLUMN)) {\r
+            throw new IllegalArgumentException("Invalid value passed to the " +\r
+                    "resizeBehaviour parameter of AddDimensionedImage.addImageToSheet()");\r
+        }\r
+\r
+        // Call methods to calculate how the image and sheet should be\r
+        // manipulated to accomodate the image; columns and then rows.\r
+        colClientAnchorDetail = this.fitImageToColumns(sheet, colNumber,\r
+                reqImageWidthMM, resizeBehaviour);\r
+        rowClientAnchorDetail = this.fitImageToRows(sheet, rowNumber,\r
+                reqImageHeightMM, resizeBehaviour);\r
+\r
+        // Having determined if and how to resize the rows, columns and/or the\r
+        // image, create the ClientAnchor object to position the image on\r
+        // the worksheet. Note how the two ClientAnchorDetail records are\r
+        // interrogated to recover the row/column co-ordinates and any insets.\r
+        // The first two parameters are not used currently but could be if the\r
+        // need arose to extend the functionality of this code by adding the\r
+        // ability to specify that a clear 'border' be placed around the image.\r
+        anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor();\r
+\r
+        anchor.setDx1(0);\r
+        anchor.setDy1(0);\r
+        anchor.setDx2(colClientAnchorDetail.getInset());\r
+        anchor.setDy2(rowClientAnchorDetail.getInset());\r
+        anchor.setCol1(colClientAnchorDetail.getFromIndex());\r
+        anchor.setRow1(rowClientAnchorDetail.getFromIndex());\r
+        anchor.setCol2(colClientAnchorDetail.getToIndex());\r
+        anchor.setRow2(rowClientAnchorDetail.getToIndex());\r
+\r
+        // For now, set the anchor type to do not move or resize the\r
+        // image as the size of the row/column is adjusted. This could easilly\r
+        // become another parameter passed to the method. Please read the note\r
+        // above regarding the behaviour of image resizing.\r
+        anchor.setAnchorType(ClientAnchor.MOVE_AND_RESIZE);\r
+\r
+        // Now, add the picture to the workbook. Note that unlike the similar\r
+        // method in the HSSF Examples section, the image type is checked. First,\r
+        // the image files location is identified by interrogating the URL passed\r
+        // to the method, the images type is identified before it is added to the\r
+        // sheet.\r
+        String sURL = imageFile.toString().toLowerCase();\r
+               if( sURL.endsWith(".png") ) {\r
+                       imageType = Workbook.PICTURE_TYPE_PNG;\r
+               }\r
+               else if( sURL.endsWith("jpg") || sURL.endsWith(".jpeg") ) {\r
+                       imageType = Workbook.PICTURE_TYPE_JPEG;\r
+               }\r
+               else  {\r
+                       throw new IllegalArgumentException("Invalid Image file extension: " +\r
+                               FilenameUtils.getExtension(sURL));\r
+               }\r
+        int index = sheet.getWorkbook().addPicture(\r
+               IOUtils.toByteArray(imageFile.openStream()),type);\r
+        drawing.createPicture(anchor, index);\r
+    }\r
+\r
+    /**\r
+     * Determines whether the sheets columns should be re-sized to accomodate\r
+     * the image, adjusts the columns width if necessary and creates then\r
+     * returns a ClientAnchorDetail object that facilitates construction of\r
+     * an ClientAnchor that will fix the image on the sheet and establish\r
+     * it's size.\r
+     *\r
+     * @param sheet A reference to the sheet that will 'contain' the image.\r
+     * @param colNumber A primtive int that contains the index number of a\r
+     *                  column on the sheet.\r
+     * @param reqImageWidthMM A primtive double that contains the required\r
+     *                        width of the image in millimetres\r
+     * @param resizeBehaviour A primitve int whose value will indicate how the\r
+     *                        width of the column should be adjusted if the\r
+     *                        required width of the image is greater than the\r
+     *                        width of the column.\r
+     * @return An instance of the ClientAnchorDetail class that will contain\r
+     *         the index number of the column containing the cell whose top\r
+     *         left hand corner also defines the top left hand corner of the\r
+     *         image, the index number column containing the cell whose top\r
+     *         left hand corner also defines the bottom right hand corner of\r
+     *         the image and an inset that determines how far the right hand\r
+     *         edge of the image can protrude into the next column - expressed\r
+     *         as a specific number of co-ordinate positions.\r
+     */\r
+    private ClientAnchorDetail fitImageToColumns(Sheet sheet, int colNumber,\r
+            double reqImageWidthMM, int resizeBehaviour) {\r
+\r
+        double colWidthMM = 0.0D;\r
+        double colCoordinatesPerMM = 0.0D;\r
+        int pictureWidthCoordinates = 0;\r
+        ClientAnchorDetail colClientAnchorDetail = null;\r
+\r
+        // Get the colum's width in millimetres\r
+        colWidthMM = ConvertImageUnits.widthUnits2Millimetres(\r
+                (short)sheet.getColumnWidth(colNumber));\r
+\r
+        // Check that the column's width will accomodate the image at the\r
+        // required dimension. If the width of the column is LESS than the\r
+        // required width of the image, decide how the application should\r
+        // respond - resize the column or overlay the image across one or more\r
+        // columns.\r
+        if(colWidthMM < reqImageWidthMM) {\r
+\r
+            // Should the column's width simply be expanded?\r
+            if((resizeBehaviour == AddDimensionedImage.EXPAND_COLUMN) ||\r
+               (resizeBehaviour == AddDimensionedImage.EXPAND_ROW_AND_COLUMN)) {\r
+                // Set the width of the column by converting the required image\r
+                // width from millimetres into Excel's column width units.\r
+                sheet.setColumnWidth(colNumber,\r
+                        ConvertImageUnits.millimetres2WidthUnits(reqImageWidthMM));\r
+                // To make the image occupy the full width of the column, convert\r
+                // the required width of the image into co-ordinates. This value\r
+                // will become the inset for the ClientAnchorDetail class that\r
+                // is then instantiated.\r
+                colWidthMM = reqImageWidthMM;\r
+                colCoordinatesPerMM = ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS /\r
+                    colWidthMM;\r
+                pictureWidthCoordinates = (int)(reqImageWidthMM * colCoordinatesPerMM);\r
+                colClientAnchorDetail = new ClientAnchorDetail(colNumber,\r
+                        colNumber, pictureWidthCoordinates);\r
+            }\r
+            // If the user has chosen to overlay both rows and columns or just\r
+            // to expand ONLY the size of the rows, then calculate how to lay\r
+            // the image out across one or more columns.\r
+            else if ((resizeBehaviour == AddDimensionedImage.OVERLAY_ROW_AND_COLUMN) ||\r
+                     (resizeBehaviour == AddDimensionedImage.EXPAND_ROW)) {\r
+                colClientAnchorDetail = this.calculateColumnLocation(sheet,\r
+                        colNumber, reqImageWidthMM);\r
+            }\r
+        }\r
+        // If the column is wider than the image.\r
+        else {\r
+            // Mow many co-ordinate positions are there per millimetre?\r
+            colCoordinatesPerMM = ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS /\r
+                    colWidthMM;\r
+            // Given the width of the image, what should be it's co-ordinate?\r
+            pictureWidthCoordinates = (int)(reqImageWidthMM * colCoordinatesPerMM);\r
+            colClientAnchorDetail = new ClientAnchorDetail(colNumber,\r
+                    colNumber, pictureWidthCoordinates);\r
+        }\r
+        return(colClientAnchorDetail);\r
+    }\r
+\r
+    /**\r
+     * Determines whether the sheet's row should be re-sized to accomodate\r
+     * the image, adjusts the rows height if necessary and creates then\r
+     * returns a ClientAnchorDetail object that facilitates construction of\r
+     * a ClientAnchor that will fix the image on the sheet and establish\r
+     * it's size.\r
+     *\r
+     * @param sheet A reference to the sheet that will 'contain' the image.\r
+     * @param rowNumber A primtive int that contains the index number of a\r
+     *                  row on the sheet.\r
+     * @param reqImageHeightMM A primtive double that contains the required\r
+     *                         height of the image in millimetres\r
+     * @param resizeBehaviour A primitve int whose value will indicate how the\r
+     *                        height of the row should be adjusted if the\r
+     *                        required height of the image is greater than the\r
+     *                        height of the row.\r
+     * @return An instance of the ClientAnchorDetail class that will contain\r
+     *         the index number of the row containing the cell whose top\r
+     *         left hand corner also defines the top left hand corner of the\r
+     *         image, the index number of the row containing the cell whose\r
+     *         top left hand corner also defines the bottom right hand\r
+     *         corner of the image and an inset that determines how far the\r
+     *         bottom edge of the image can protrude into the next (lower)\r
+     *         row - expressed as a specific number of co-ordinate positions.\r
+     */\r
+    private ClientAnchorDetail fitImageToRows(Sheet sheet, int rowNumber,\r
+            double reqImageHeightMM, int resizeBehaviour) {\r
+        Row row = null;\r
+        double rowHeightMM = 0.0D;\r
+        double rowCoordinatesPerMM = 0.0D;\r
+        int pictureHeightCoordinates = 0;\r
+        ClientAnchorDetail rowClientAnchorDetail = null;\r
+\r
+        // Get the row and it's height\r
+        row = sheet.getRow(rowNumber);\r
+        if(row == null) {\r
+            // Create row if it does not exist.\r
+            row = sheet.createRow(rowNumber);\r
+        }\r
+\r
+        // Get the row's height in millimetres\r
+        rowHeightMM = row.getHeightInPoints() / ConvertImageUnits.POINTS_PER_MILLIMETRE;\r
+\r
+        // Check that the row's height will accomodate the image at the required\r
+        // dimensions. If the height of the row is LESS than the required height\r
+        // of the image, decide how the application should respond - resize the\r
+        // row or overlay the image across a series of rows.\r
+        if(rowHeightMM < reqImageHeightMM) {\r
+            if((resizeBehaviour == AddDimensionedImage.EXPAND_ROW) ||\r
+               (resizeBehaviour == AddDimensionedImage.EXPAND_ROW_AND_COLUMN)) {\r
+                row.setHeightInPoints((float)(reqImageHeightMM *\r
+                        ConvertImageUnits.POINTS_PER_MILLIMETRE));\r
+                rowHeightMM = reqImageHeightMM;\r
+                rowCoordinatesPerMM = ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS /\r
+                    rowHeightMM;\r
+                pictureHeightCoordinates = (int)(reqImageHeightMM * rowCoordinatesPerMM);\r
+                rowClientAnchorDetail = new ClientAnchorDetail(rowNumber,\r
+                        rowNumber, pictureHeightCoordinates);\r
+            }\r
+            // If the user has chosen to overlay both rows and columns or just\r
+            // to expand ONLY the size of the columns, then calculate how to lay\r
+            // the image out ver one or more rows.\r
+            else if((resizeBehaviour == AddDimensionedImage.OVERLAY_ROW_AND_COLUMN) ||\r
+                    (resizeBehaviour == AddDimensionedImage.EXPAND_COLUMN)) {\r
+                rowClientAnchorDetail = this.calculateRowLocation(sheet,\r
+                        rowNumber, reqImageHeightMM);\r
+            }\r
+        }\r
+        // Else, if the image is smaller than the space available\r
+        else {\r
+            rowCoordinatesPerMM = ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS /\r
+                    rowHeightMM;\r
+            pictureHeightCoordinates = (int)(reqImageHeightMM * rowCoordinatesPerMM);\r
+            rowClientAnchorDetail = new ClientAnchorDetail(rowNumber,\r
+                        rowNumber, pictureHeightCoordinates);\r
+        }\r
+        return(rowClientAnchorDetail);\r
+    }\r
+\r
+    /**\r
+     * If the image is to overlie more than one column, calculations need to be\r
+     * performed to determine how many columns and whether the image will\r
+     * overlie just a part of one column in order to be presented at the\r
+     * required size.\r
+     *\r
+     * @param sheet The sheet that will 'contain' the image.\r
+     * @param startingColumn A primitive int whose value is the index of the\r
+     *                       column that contains the cell whose top left hand\r
+     *                       corner should be aligned with the top left hand\r
+     *                       corner of the image.\r
+     * @param reqImageWidthMM A primitive double whose value will indicate the\r
+     *                        required width of the image in millimetres.\r
+     * @return An instance of the ClientAnchorDetail class that will contain\r
+     *         the index number of the column containing the cell whose top\r
+     *         left hand corner also defines the top left hand corner of the\r
+     *         image, the index number column containing the cell whose top\r
+     *         left hand corner also defines the bottom right hand corner of\r
+     *         the image and an inset that determines how far the right hand\r
+     *         edge of the image can protrude into the next column - expressed\r
+     *         as a specific number of co-ordinate positions.\r
+     */\r
+    private ClientAnchorDetail calculateColumnLocation(Sheet sheet,\r
+                                                       int startingColumn,\r
+                                                       double reqImageWidthMM) {\r
+        ClientAnchorDetail anchorDetail = null;\r
+        double totalWidthMM = 0.0D;\r
+        double colWidthMM = 0.0D;\r
+        double overlapMM = 0.0D;\r
+        double coordinatePositionsPerMM = 0.0D;\r
+        int toColumn = startingColumn;\r
+        int inset = 0;\r
+\r
+        // Calculate how many columns the image will have to\r
+        // span in order to be presented at the required size.\r
+        while(totalWidthMM < reqImageWidthMM) {\r
+            colWidthMM = ConvertImageUnits.widthUnits2Millimetres(\r
+                    (short)(sheet.getColumnWidth(toColumn)));\r
+            // Note use of the cell border width constant. Testing with an image\r
+            // declared to fit exactly into one column demonstrated that it's\r
+            // width was greater than the width of the column the POI returned.\r
+            // Further, this difference was a constant value that I am assuming\r
+            // related to the cell's borders. Either way, that difference needs\r
+            // to be allowed for in this calculation.\r
+            totalWidthMM += (colWidthMM + ConvertImageUnits.CELL_BORDER_WIDTH_MILLIMETRES);\r
+            toColumn++;\r
+        }\r
+        // De-crement by one the last column value.\r
+        toColumn--;\r
+        // Highly unlikely that this will be true but, if the width of a series\r
+        // of columns is exactly equal to the required width of the image, then\r
+        // simply build a ClientAnchorDetail object with an inset equal to the\r
+        // total number of co-ordinate positions available in a column, a\r
+        // from column co-ordinate (top left hand corner) equal to the value\r
+        // of the startingColumn parameter and a to column co-ordinate equal\r
+        // to the toColumn variable.\r
+        //\r
+        // Convert both values to ints to perform the test.\r
+        if((int)totalWidthMM == (int)reqImageWidthMM) {\r
+            // A problem could occur if the image is sized to fit into one or\r
+            // more columns. If that occurs, the value in the toColumn variable\r
+            // will be in error. To overcome this, there are two options, to\r
+            // ibcrement the toColumn variable's value by one or to pass the\r
+            // total number of co-ordinate positions to the third paramater\r
+            // of the ClientAnchorDetail constructor. For no sepcific reason,\r
+            // the latter option is used below.\r
+            anchorDetail = new ClientAnchorDetail(startingColumn,\r
+                    toColumn, ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS);\r
+        }\r
+        // In this case, the image will overlap part of another column and it is\r
+        // necessary to calculate just how much - this will become the inset\r
+        // for the ClientAnchorDetail object.\r
+        else {\r
+            // Firstly, claculate how much of the image should overlap into\r
+            // the next column.\r
+            overlapMM = reqImageWidthMM - (totalWidthMM - colWidthMM);\r
+\r
+            // When the required size is very close indded to the column size,\r
+            // the calcaulation above can produce a negative value. To prevent\r
+            // problems occuring in later caculations, this is simply removed\r
+            // be setting the overlapMM value to zero.\r
+            if(overlapMM < 0) {\r
+                overlapMM = 0.0D;\r
+            }\r
+\r
+            // Next, from the columns width, calculate how many co-ordinate\r
+            // positons there are per millimetre\r
+            coordinatePositionsPerMM = ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS /\r
+                    colWidthMM;\r
+            // From this figure, determine how many co-ordinat positions to\r
+            // inset the left hand or bottom edge of the image.\r
+            inset = (int)(coordinatePositionsPerMM * overlapMM);\r
+\r
+            // Now create the ClientAnchorDetail object, setting the from and to\r
+            // columns and the inset.\r
+            anchorDetail = new ClientAnchorDetail(startingColumn, toColumn, inset);\r
+        }\r
+        return(anchorDetail);\r
+    }\r
+\r
+    /**\r
+     * If the image is to overlie more than one rows, calculations need to be\r
+     * performed to determine how many rows and whether the image will\r
+     * overlie just a part of one row in order to be presented at the\r
+     * required size.\r
+     *\r
+     * @param sheet The sheet that will 'contain' the image.\r
+     * @param startingRow A primitive int whose value is the index of the row\r
+     *                    that contains the cell whose top left hand corner\r
+     *                    should be aligned with the top left hand corner of\r
+     *                    the image.\r
+     * @param reqImageHeightMM A primitive double whose value will indicate the\r
+     *                         required height of the image in millimetres.\r
+     * @return An instance of the ClientAnchorDetail class that will contain\r
+     *         the index number of the row containing the cell whose top\r
+     *         left hand corner also defines the top left hand corner of the\r
+     *         image, the index number of the row containing the cell whose top\r
+     *         left hand corner also defines the bottom right hand corner of\r
+     *         the image and an inset that determines how far the bottom edge\r
+     *         can protrude into the next (lower) row - expressed as a specific\r
+     *         number of co-ordinate positions.\r
+     */\r
+    private ClientAnchorDetail calculateRowLocation(Sheet sheet,\r
+            int startingRow, double reqImageHeightMM) {\r
+        ClientAnchorDetail clientAnchorDetail = null;\r
+        Row row = null;\r
+        double rowHeightMM = 0.0D;\r
+        double totalRowHeightMM = 0.0D;\r
+        double overlapMM = 0.0D;\r
+        double rowCoordinatesPerMM = 0.0D;\r
+        int toRow = startingRow;\r
+        int inset = 0;\r
+\r
+        // Step through the rows in the sheet and accumulate a total of their\r
+        // heights.\r
+        while(totalRowHeightMM < reqImageHeightMM) {\r
+            row = sheet.getRow(toRow);\r
+            // Note, if the row does not already exist on the sheet then create\r
+            // it here.\r
+            if(row == null) {\r
+                row = sheet.createRow(toRow);\r
+            }\r
+            // Get the row's height in millimetres and add to the running total.\r
+            rowHeightMM = row.getHeightInPoints() /\r
+                    ConvertImageUnits.POINTS_PER_MILLIMETRE;\r
+            totalRowHeightMM += rowHeightMM;\r
+            toRow++;\r
+        }\r
+        // Owing to the way the loop above works, the rowNumber will have been\r
+        // incremented one row too far. Undo that here.\r
+        toRow--;\r
+        // Check to see whether the image should occupy an exact number of\r
+        // rows. If so, build the ClientAnchorDetail record to point\r
+        // to those rows and with an inset of the total number of co-ordinate\r
+        // position in the row.\r
+        //\r
+        // To overcome problems that can occur with comparing double values for\r
+        // equality, cast both to int(s) to truncate the value; VERY crude and\r
+        // I do not really like it!!\r
+        if((int)totalRowHeightMM == (int)reqImageHeightMM) {\r
+            clientAnchorDetail = new ClientAnchorDetail(startingRow, toRow,\r
+                    ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS);\r
+        }\r
+        else {\r
+            // Calculate how far the image will project into the next row. Note\r
+            // that the height of the last row assessed is subtracted from the\r
+            // total height of all rows assessed so far.\r
+            overlapMM = reqImageHeightMM - (totalRowHeightMM - rowHeightMM);\r
+\r
+            // To prevent an exception being thrown when the required width of\r
+            // the image is very close indeed to the column size.\r
+            if(overlapMM < 0) {\r
+                overlapMM = 0.0D;\r
+            }\r
+\r
+            rowCoordinatesPerMM = ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS /\r
+                    rowHeightMM;\r
+            inset = (int)(overlapMM * rowCoordinatesPerMM);\r
+            clientAnchorDetail = new ClientAnchorDetail(startingRow,\r
+                        toRow, inset);\r
+        }\r
+        return(clientAnchorDetail);\r
+    }\r
+\r
+    /**\r
+     * The main entry point to the program. It contains code that demonstrates\r
+     * one way to use the program.\r
+     *\r
+     * Note, the code is not restricted to use on new workbooks only. If an\r
+     * image is to be inserted into an existing workbook. just open that\r
+     * workbook, gat a reference to a sheet and pass that;\r
+     *\r
+     *      AddDimensionedImage addImage = new AddDimensionedImage();\r
+     *\r
+     *      File file = new File("....... Existing Workbook .......");\r
+     *      FileInputStream fis = new FileInputStream(file);\r
+     *      Workbook workbook = new HSSFWorkbook(fis);\r
+     *      HSSFSheet sheet = workbook.getSheetAt(0);\r
+     *      addImage.addImageToSheet("C3", sheet, "image.jpg", 30, 20,\r
+     *          AddDimensionedImage.EXPAND.ROW);\r
+     *\r
+     * @param args the command line arguments\r
+     */\r
+    public static void main(String[] args) {\r
+       String imageFile = null;\r
+       String outputFile = null;\r
+        FileOutputStream fos = null;\r
+        Workbook workbook = null;\r
+        Sheet sheet = null;\r
+        try {\r
+               if(args.length < 2){\r
+                       System.err.println("Usage: AddDimensionedImage imageFile outputFile");\r
+                       return;\r
+               }\r
+               workbook = new HSSFWorkbook();\r
+               sheet = workbook.createSheet("Picture Test");\r
+            // Note that as the code has been ported to the SS model, the following\r
+            // would be equally as valid - workbook = new XSSFWorkbook();\r
+               imageFile = args[0];\r
+               outputFile = args[1];\r
+               new AddDimensionedImage().addImageToSheet("B5", sheet, sheet.createDrawingPatriarch(),\r
+                       new File(imageFile).toURI().toURL(), 100, 40,\r
+                       AddDimensionedImage.EXPAND_ROW_AND_COLUMN);\r
+               fos = new FileOutputStream(outputFile);\r
+            workbook.write(fos);\r
+        }\r
+        catch(FileNotFoundException fnfEx) {\r
+            System.out.println("Caught an: " + fnfEx.getClass().getName());\r
+            System.out.println("Message: " + fnfEx.getMessage());\r
+            System.out.println("Stacktrace follows...........");\r
+            fnfEx.printStackTrace(System.out);\r
+        }\r
+        catch(IOException ioEx) {\r
+            System.out.println("Caught an: " + ioEx.getClass().getName());\r
+            System.out.println("Message: " + ioEx.getMessage());\r
+            System.out.println("Stacktrace follows...........");\r
+            ioEx.printStackTrace(System.out);\r
+        }\r
+        finally {\r
+            if(fos != null) {\r
+                try {\r
+                    fos.close();\r
+                    fos = null;\r
+                }\r
+                catch(IOException ioEx) {\r
+                    // I G N O R E\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * The HSSFClientAnchor class accepts eight parameters. In order, these are;\r
+     *\r
+     *      * How far the left hand edge of the image is inset from the left hand\r
+     *      edge of the cell\r
+     *      * How far the top edge of the image is inset from the top of the cell\r
+     *      * How far the right hand edge of the image is inset from the left\r
+     *      hand edge of the cell\r
+     *      * How far the bottom edge of the image is inset from the top of the\r
+     *      cell.\r
+     *      * Together, parameters five and six determine the column and row\r
+     *      co-ordinates of the cell whose top left hand corner will be aligned\r
+     *      with the image's top left hand corner.\r
+     *      * Together, parameter seven and eight determine the column and row\r
+     *      co-ordinates of the cell whose top left hand corner will be aligned\r
+     *      with the images bottom right hand corner.\r
+     *\r
+     * An instance of the ClientAnchorDetail class provides three of the eight\r
+     * parameters, one of the co-ordinates for the images top left hand corner,\r
+     * one of the co-ordinates for the images bottom right hand corner and\r
+     * either how far the image should be inset from the top or the left hand\r
+     * edge of the cell.\r
+     *\r
+     * @author Mark Beardsley [msb at apache.org]\r
+     * @version 1.00 5th August 2009.\r
+     */\r
+    public class ClientAnchorDetail {\r
+\r
+        public int fromIndex = 0;\r
+        public int toIndex = 0;\r
+        public int inset = 0;\r
+\r
+        /**\r
+         * Create a new instance of the ClientAnchorDetail class using the\r
+         * following parameters.\r
+         *\r
+         * @param fromIndex A primitive int that contains one of the\r
+         *                  co-ordinates (row or column index) for the top left\r
+         *                  hand corner of the image.\r
+         * @param toIndex A primitive int that contains one of the\r
+         *                co-ordinates (row or column index) for the bottom\r
+         *                right hand corner of the image.\r
+         * @param inset A primitive int that contains a value which indicates\r
+         *              how far the image should be inset from the top or the\r
+         *              left hand edge of a cell.\r
+         */\r
+        public ClientAnchorDetail(int fromIndex, int toIndex, int inset) {\r
+            this.fromIndex = fromIndex;\r
+            this.toIndex = toIndex;\r
+            this.inset = inset;\r
+        }\r
+\r
+        /**\r
+         * Get one of the number of the column or row that contains the cell\r
+         * whose top left hand corner will be aligned with the top left hand\r
+         * corner of the image.\r
+         *\r
+         * @return The value - row or column index - for one of the co-ordinates\r
+         *         of the top left hand corner of the image.\r
+         */\r
+        public int getFromIndex() {\r
+            return(this.fromIndex);\r
+        }\r
+\r
+        /**\r
+         * Get one of the number of the column or row that contains the cell\r
+         * whose top left hand corner will be aligned with the bottom righ hand\r
+         * corner of the image.\r
+         *\r
+         * @return The value - row or column index - for one of the co-ordinates\r
+         *         of the bottom right hand corner of the image.\r
+         */\r
+        public int getToIndex() {\r
+            return(this.toIndex);\r
+        }\r
+\r
+        /**\r
+         * Get the image's offset from the edge of a cell.\r
+         *\r
+         * @return How far either the right hand or bottom edge of the image is\r
+         *         inset from the left hand or top edge of a cell.\r
+         */\r
+        public int getInset() {\r
+            return(this.inset);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Utility methods used to convert Excel's character based column and row\r
+     * size measurements into pixels and/or millimetres. The class also contains\r
+     * various constants that are required in other calculations.\r
+     *\r
+     * @author xio[darjino@hotmail.com]\r
+     * @version 1.01 30th July 2009.\r
+     *      Added by Mark Beardsley [msb at apache.org].\r
+     *          Additional constants.\r
+     *          widthUnits2Millimetres() and millimetres2Units() methods.\r
+     */\r
+    public static class ConvertImageUnits {\r
+\r
+        // Each cell conatins a fixed number of co-ordinate points; this number\r
+        // does not vary with row height or column width or with font. These two\r
+        // constants are defined below.\r
+        public static final int TOTAL_COLUMN_COORDINATE_POSITIONS = 1023; // MB\r
+        public static final int TOTAL_ROW_COORDINATE_POSITIONS = 255;     // MB\r
+        // The resoultion of an image can be expressed as a specific number\r
+        // of pixels per inch. Displays and printers differ but 96 pixels per\r
+        // inch is an acceptable standard to beging with.\r
+        public static final int PIXELS_PER_INCH = 96;                     // MB\r
+        // Cnstants that defines how many pixels and points there are in a\r
+        // millimetre. These values are required for the conversion algorithm.\r
+        public static final double PIXELS_PER_MILLIMETRES = 3.78;         // MB\r
+        public static final double POINTS_PER_MILLIMETRE = 2.83;          // MB\r
+        // The column width returned by HSSF and the width of a picture when\r
+        // positioned to exactly cover one cell are different by almost exactly\r
+        // 2mm - give or take rounding errors. This constant allows that\r
+        // additional amount to be accounted for when calculating how many\r
+        // celles the image ought to overlie.\r
+        public static final double CELL_BORDER_WIDTH_MILLIMETRES = 2.0D;  // MB\r
+        public static final short EXCEL_COLUMN_WIDTH_FACTOR = 256;\r
+        public static final int UNIT_OFFSET_LENGTH = 7;\r
+        public static final int[] UNIT_OFFSET_MAP = new int[]\r
+            { 0, 36, 73, 109, 146, 182, 219 };\r
+\r
+        /**\r
+        * pixel units to excel width units(units of 1/256th of a character width)\r
+        * @param pxs\r
+        * @return\r
+        */\r
+        public static short pixel2WidthUnits(int pxs) {\r
+            short widthUnits = (short) (EXCEL_COLUMN_WIDTH_FACTOR *\r
+                    (pxs / UNIT_OFFSET_LENGTH));\r
+            widthUnits += UNIT_OFFSET_MAP[(pxs % UNIT_OFFSET_LENGTH)];\r
+            return widthUnits;\r
+        }\r
+\r
+        /**\r
+         * excel width units(units of 1/256th of a character width) to pixel\r
+         * units.\r
+         *\r
+         * @param widthUnits\r
+         * @return\r
+         */\r
+        public static int widthUnits2Pixel(short widthUnits) {\r
+            int pixels = (widthUnits / EXCEL_COLUMN_WIDTH_FACTOR)\r
+                    * UNIT_OFFSET_LENGTH;\r
+            int offsetWidthUnits = widthUnits % EXCEL_COLUMN_WIDTH_FACTOR;\r
+            pixels += Math.round(offsetWidthUnits /\r
+                    ((float) EXCEL_COLUMN_WIDTH_FACTOR / UNIT_OFFSET_LENGTH));\r
+            return pixels;\r
+        }\r
+\r
+        /**\r
+         * Convert Excel's width units into millimetres.\r
+         *\r
+         * @param widthUnits The width of the column or the height of the\r
+         *                   row in Excel's units.\r
+         * @return A primitive double that contains the columns width or rows\r
+         *         height in millimetres.\r
+         */\r
+        public static double widthUnits2Millimetres(short widthUnits) {\r
+            return(ConvertImageUnits.widthUnits2Pixel(widthUnits) /\r
+                   ConvertImageUnits.PIXELS_PER_MILLIMETRES);\r
+        }\r
+\r
+        /**\r
+         * Convert into millimetres Excel's width units..\r
+         *\r
+         * @param millimetres A primitive double that contains the columns\r
+         *                    width or rows height in millimetres.\r
+         * @return A primitive int that contains the columns width or rows\r
+         *         height in Excel's units.\r
+         */\r
+        public static int millimetres2WidthUnits(double millimetres) {\r
+            return(ConvertImageUnits.pixel2WidthUnits((int)(millimetres *\r
+                    ConvertImageUnits.PIXELS_PER_MILLIMETRES)));\r
+        }\r
+        \r
+        public static int pointsToPixels(double points) {\r
+               return (int) Math.round(points / 72D * PIXELS_PER_INCH);\r
+       }\r
+        \r
+        public static double pointsToMillimeters(double points) {\r
+               return points / 72D * 25.4;\r
+        }\r
+    }\r
+}
\ No newline at end of file