# MetafileHelper.java

    /*
    ** Module   : MetafileHelper.java
    ** Abstract : Helper class to provide report generation from generic GUI metafile.
    **
    ** Copyright (c) 2019-2024, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --------------------------------------Description---------------------------------------
    ** 001 EVL 20190510 Created initial version.
    ** 002 EVL 20190618 Removed printDoc and sendOptionalText from metafile extension set.
    ** 003 EVL 20201216 Added more clean up for metafile stop/interrupt processing.
    **     EVL 20210106 Added code to properly finalize the metafile recording.
    **     EVL 20210107 Added new helper methods to get current temporary directory and reset text styles.
    **     EVL 20210113 Added new helper method to calculate text scaling factor.
    **     EVL 20210114 Changed to use common constant from Xpr2PdfWorker.
    **     EVL 20210602 Added setPageOrientation(character) method.
    **     GES 20210610 Added setTextStyle(character) method.
    **     EVL 20211021 Making small improvement for text width calculation with Arial font.
    **     EVL 20211026 Changing text font size type to float.
    **     EVL 20211115 Improved handling of the ' ' char weight while text width calculation.
    **     EVL 20220126 Some fixes for text width calculation approach.
    **     EVL 20220127 Adding method to get current text underline attribute.  Fix text width for certain very
    **                  narrow chars.
    **     EVL 20220227 More separations for wide, extra wide and regular characters fro capital letters.
    **     EVL 20220228 Adding methods version with 4GL compatible input parameters types.
    **     EVL 20220302 Moving here the additional processing for bold and italic fonts.
    **     EVL 20220314 Adding two more characters to very narrow character set for weight calculation.
    **     EVL 20220315 Reviewed printable characters size based on general sample and changes some sets to
    **                  increase match and precision.
    ** 004 DDF 20230620 Replaced static initialization of values from the directory configuration with
    **                  a method called at server bootstrap.
    ** 005 EVL 20240730 Adding ability to have preview only instead of real file downloading.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.reporting;

    import com.goldencode.p2j.security.*;
    import com.goldencode.p2j.ui.client.*;
    import com.goldencode.p2j.util.*;

    import java.io.*;

    /**
     * A helper class to convert and show a generic GDI metafile as a PDF document. This replacement is platform
     * neutral (not dependent upon WIN32).
     */
    public class MetafileHelper
    {
       /** Base units is inches constant. */
       private static final int UNITS_INCHES = 0;

       /** Base units is cm constant. */
       private static final int UNITS_CM = 1;

       /** Base units is mm constant. */
       private static final int UNITS_MM = 2;

       /** Row modifier to convert XPR helper row coords to pixel. */
       private static double pixPerRow = XprHelper.getRow2Pix(Xpr2PdfWorker.DPI_DEFAULT,
                                                              XprEntity.LPI_DEFAULT);

       /** Column modifier to convert XPR helper character coords to pixel. */
       private static double pixPerCol = XprHelper.getCol2Pix(Xpr2PdfWorker.DPI_DEFAULT,
                                                              XprEntity.CPI_DEFAULT);

       /** Base units type, inches, cm or mm. */
       private static int units = UNITS_CM;

       /** Stores context-local state variables. */
       private static ContextContainer work = new ContextContainer();

       /** Flag to open real file or make preview. */
       private static boolean openFile = true;

       /**
        * Method called at server bootstrap that initializes values from the directory
        * configuration. Until this method is called, default values are used.
        */
       public static void bootstrap()
       {
          // several options can be overridden in directory
          // metafile units override
          String unitsOverride = Utils.getDirectoryNodeString(null, "metafile-report/units", null,
                                                              Utils.DirScope.BOTH, null);
          if (unitsOverride != null && !unitsOverride.isEmpty())
          {
             if(unitsOverride.equalsIgnoreCase("INCHES"))
             {
                units = UNITS_INCHES;
             }
             else if(unitsOverride.equalsIgnoreCase("MM"))
             {
                units = UNITS_MM;
             }
             else if(unitsOverride.equalsIgnoreCase("CM"))
             {
                units = UNITS_CM;
             }
          }
       }

       /**
        * Sets new values for text alignment.
        *
        * @param    horizAlign
        *           The new String value for horizontal text alignment.  The valid alignment values:
        *           "left", "center", "right".
        * @param    vertAlign
        *           The new String value for vertical text alignment.  The valid alignment values:
        *           "top", "baseline", "bottom".
        */
       public static void setTextAlignment(String horizAlign, String vertAlign)
       {
          resolveCurrentMetafile().setTextAlignment(horizAlign, vertAlign);
       }

       /**
        * Sets new values for text alignment.
        *
        * @param    horizAlign
        *           The new String value for horizontal text alignment.  The valid alignment values:
        *           "left", "center", "right".
        * @param    vertAlign
        *           The new String value for vertical text alignment.  The valid alignment values:
        *           "top", "baseline", "bottom".
        */
       public static void setTextAlignment(character horizAlign, String vertAlign)
       {
          if (isParameterValid(horizAlign))
          {
             setTextAlignment(horizAlign.getValue(), vertAlign);
          }
       }

       /**
        * Sets new values for text alignment.
        *
        * @param    horizAlign
        *           The new String value for horizontal text alignment.  The valid alignment values:
        *           "left", "center", "right".
        * @param    vertAlign
        *           The new String value for vertical text alignment.  The valid alignment values:
        *           "top", "baseline", "bottom".
        */
       public static void setTextAlignment(String horizAlign, character vertAlign)
       {
          if (isParameterValid(vertAlign))
          {
             setTextAlignment(horizAlign, vertAlign.getValue());
          }
       }

       /**
        * Sets new values for text alignment.
        *
        * @param    horizAlign
        *           The new String value for horizontal text alignment.  The valid alignment values:
        *           "left", "center", "right".
        * @param    vertAlign
        *           The new String value for vertical text alignment.  The valid alignment values:
        *           "top", "baseline", "bottom".
        */
       public static void setTextAlignment(character horizAlign, character vertAlign)
       {
          if (isParameterValid(horizAlign) && isParameterValid(vertAlign))
          {
             setTextAlignment(horizAlign.getValue(), vertAlign.getValue());
          }
       }

       /**
        * Sets new current color value to be used in text primitives.
        *
        * @param    rgbColor
        *           The new 32-bit color value packed in int value.
        */
       public static void setTextColor(int rgbColor)
       {
          resolveCurrentMetafile().setTextColor(rgbColor);
       }

       /**
        * Sets new current color value to be used in text primitives.
        *
        * @param    rgbColor
        *           The new 32-bit color value packed in int value.
        */
       public static void setTextColor(integer rgbColor)
       {
          if (isParameterValid(rgbColor))
          {
             setTextColor(rgbColor.intValue());
          }
       }

       /**
        * Sets new value for text style.
        *
        * @param    style
        *           The new String value for the current text style.  The valid style values:
        *           "italic", "underline", "bold", "no-italic", "no-underline", "no-bold".
        */
       public static void setTextStyle(String style)
       {
          resolveCurrentMetafile().setTextStyle(style);
       }

       /**
        * Sets new value for text style.
        *
        * @param    style
        *           The new String value for the current text style.  The valid style values:
        *           "italic", "underline", "bold", "no-italic", "no-underline", "no-bold".
        */
       public static void setTextStyle(character style)
       {
          if (style == null || style.isUnknown())
          {
             return;
          }

          setTextStyle(style.toStringMessage());
       }

       /**
        * Gets flag meaning underlined text should be currently used.
        *
        * @return   <code>TRUE</code> if text underlined, <code>FALSE</code> otherwise.
        */
       public static boolean isUnderline()
       {
          return resolveCurrentMetafile().isUnderline();
       }

       /**
        * Resets new value for text style to be off.
        */
       public static void resetTextStyle()
       {
          resolveCurrentMetafile().resetTextStyle();
       }

       /**
        * Starts the new page construction for the document that is currently processing.
        */
       public static void startNewPage()
       {
          resolveCurrentMetafile().startNewPage();
       }

       /**
        * Sets new value for current font.
        *
        * @param    fontName
        *           The new String value for the current font name.  Can be "default" for default
        *           font to use.
        */
       public static void setFont(String fontName)
       {
          resolveCurrentMetafile().setFont(fontName);
       }

       /**
        * Sets new value for current font.
        *
        * @param    fontName
        *           The new String value for the current font name.  Can be "default" for default
        *           font to use.
        */
       public static void setFont(character fontName)
       {
          if (isParameterValid(fontName))
          {
             setFont(fontName.getValue());
          }
       }

       /**
        * Gets the current font value.
        *
        * @param    fontName
        *           The value for the font name currently in use.
        */
       public static void getFont(character fontName)
       {
          resolveCurrentMetafile().getFont(fontName);
       }

       /**
        * Sets new value for current zoom factor.
        *
        * @param    zoomType
        *           The new String value for the current zoom type.
        */
       public static void setZoomFactor(String zoomType)
       {
          resolveCurrentMetafile().setZoomFactor(zoomType);
       }

       /**
        * Sets new value for current zoom factor.
        *
        * @param    zoomType
        *           The new String value for the current zoom type.
        */
       public static void setZoomFactor(character zoomType)
       {
          if (isParameterValid(zoomType))
          {
             setZoomFactor(zoomType.getValue());
          }
       }

       /**
        * Gets the current zoom factor value.
        *
        * @param    zoomType
        *           The value for the zoom type currently in use.
        */
       public static void getZoomFactor(character zoomType)
       {
          resolveCurrentMetafile().getZoomFactor(zoomType);
       }

       /**
        * Gets the current font height value.
        *
        * @param    fontHeight
        *           The currently used font height value in metric units.
        */
       public static void getFontHeight(decimal fontHeight)
       {
          resolveCurrentMetafile().getFontHeight(fontHeight);
       }

       /**
        * Sets the current font height value.
        *
        * @param    fontHeight
        *           The font height value in pics to use.
        */
       public static void setFontHeight(float fontHeight)
       {
          resolveCurrentMetafile().setFontHeight(fontHeight);
       }

       /**
        * Sets the current font height value.
        *
        * @param    fontHeight
        *           The font height value in pics to use.
        */
       public static void setFontHeight(integer fontHeight)
       {
          if (isParameterValid(fontHeight))
          {
             setFontHeight(fontHeight.intValue());
          }
       }

       /**
        * Gets the given text width value.
        *
        * @param    textToMeasure
        *           The text to calculate width.
        * @param    textWidth
        *           The width of the text in metric units.
        */
       public static void getTextWidth(String textToMeasure, decimal textWidth)
       {
          resolveCurrentMetafile().getTextWidth(textToMeasure, textWidth);
       }

       /**
        * Gets the given text width value.
        *
        * @param    textToMeasure
        *           The text to calculate width.
        * @param    textWidth
        *           The width of the text in metric units.
        */
       public static void getTextWidth(character textToMeasure, decimal textWidth)
       {
          if (isParameterValid(textToMeasure))
          {
             getTextWidth(textToMeasure.getValue(), textWidth);
          }
       }

       /**
        * Sets the new current position for cursor.  Can be used a starting point for text/line/image
        * draw.
        *
        * @param    x
        *           The new X position.
        * @param    y
        *           The new Y position.
        */
       public static void setXY(double x, double y)
       {
          resolveCurrentMetafile().setXY(x, y);
       }

       /**
        * Sets the new current position for cursor.  Can be used a starting point for text/line/image
        * draw.
        *
        * @param    x
        *           The new X position.
        * @param    y
        *           The new Y position.
        */
       public static void setXY(decimal x, decimal y)
       {
          if (isParameterValid(x) && isParameterValid(y))
          {
             setXY(x.doubleValue(), y.doubleValue());
          }
       }

       /**
        * Sets the new current position for cursor.  Can be used a starting point for text/line/image
        * draw.
        *
        * @param    x
        *           The new X position.
        * @param    y
        *           The new Y position.
        */
       public static void setXY(double x, decimal y)
       {
          if (isParameterValid(y))
          {
             setXY(x, y.doubleValue());
          }
       }

       /**
        * Sets the new current position for cursor.  Can be used a starting point for text/line/image
        * draw.
        *
        * @param    x
        *           The new X position.
        * @param    y
        *           The new Y position.
        */
       public static void setXY(decimal x, double y)
       {
          if (isParameterValid(x))
          {
             setXY(x.doubleValue(), y);
          }
       }

       /**
        * Sets the new value for document's left margin.
        *
        * @param    leftMargin
        *           The new left margin value.
        */
       public static void setLeftMargin(double leftMargin)
       {
          resolveCurrentMetafile().setLeftMargin(leftMargin);
       }

       /**
        * Sets the new value for document's left margin.
        *
        * @param    leftMargin
        *           The new left margin value.
        */
       public static void setLeftMargin(decimal leftMargin)
       {
          if (isParameterValid(leftMargin))
          {
             setLeftMargin(leftMargin.doubleValue());
          }
       }

       /**
        * Gets the new current position for cursor.  Can be used a starting point for text/line/image
        * draw.
        *
        * @param    x
        *           The returned X position.
        * @param    y
        *           The returned Y position.
        */
       public static void getXY(decimal x, decimal y)
       {
          resolveCurrentMetafile().getXY(x, y);
       }

       /**
        * Starts new line of the text.  Y coord will be shifted by the text height and interleaving,
        * X coored be set to leftmost point of the text.
        */
       public static void startNewTextLine()
       {
          resolveCurrentMetafile().startNewTextLine();
       }

       /**
        * Sets the extra vertical space between two sequential rows.
        *
        * @param    extraYSpace
        *           The new extra Y distance between rows.
        */
       public static void setInterleaving(double extraYSpace)
       {
          resolveCurrentMetafile().setInterleaving(extraYSpace);
       }

       /**
        * Sets the extra vertical space between two sequential rows.
        *
        * @param    extraYSpace
        *           The new extra Y distance between rows.
        */
       public static void setInterleaving(decimal extraYSpace)
       {
          if (isParameterValid(extraYSpace))
          {
             setInterleaving(extraYSpace.doubleValue());
          }
       }

       /**
        * Gets the free space below the current Y position.
        *
        * @param    freeSpaceDown
        *           The remaining vertical free space on the current page.
        */
       public static void getFreeSpaceDown(decimal freeSpaceDown)
       {
          resolveCurrentMetafile().getFreeSpaceDown(freeSpaceDown);
       }

       /**
        * Gets the free space to the right the current X position.
        *
        * @param    freeSpaceRight
        *           The remaining horizontal free space on the current line(page).
        */
       public static void getFreeSpaceRight(decimal freeSpaceRight)
       {
          resolveCurrentMetafile().getFreeSpaceRight(freeSpaceRight);
       }

       /**
        * Draws the text at current position.
        *
        * @param    text
        *           The new text to draw at current position.
        */
       public static void drawText(String text)
       {
          resolveCurrentMetafile().drawText(text);
       }

       /**
        * Draws the text at current position.
        *
        * @param    text
        *           The new text to draw at current position.
        */
       public static void drawText(character text)
       {
          if (isParameterValid(text))
          {
             drawText(text.getValue());
          }
       }

       /**
        * Draws the text at current position.
        *
        * @param    x
        *           The X position to set after text drawing in device independent units.
        * @param    text
        *           The new text to draw at current position.
        */
       public static void drawTextAndSetX(double x, String text)
       {
          resolveCurrentMetafile().drawTextAndSetX(x, text);
       }

       /**
        * Draws the text at current position.
        *
        * @param    x
        *           The X position to set after text drawing in device independent units.
        * @param    text
        *           The new text to draw at current position.
        */
       public static void drawTextAndSetX(decimal x, character text)
       {
          if (isParameterValid(x) && isParameterValid(text))
          {
             drawTextAndSetX(x.doubleValue(), text.getValue());
          }
       }

       /**
        * Draws the text at the given X position and current Y position and given alignment.
        *
        * @param    x
        *           The X position for text to draw in device independent units.
        * @param    text
        *           The new text to draw at the given position.
        * @param    align
        *           The alignment value, either left or right.
        */
       public static void drawTextAtX(double x, String text, String align)
       {
          resolveCurrentMetafile().drawTextAtX(x, text, align);
       }

       /**
        * Draws the text at the given X position and current Y position and left alignment.
        *
        * @param    x
        *           The X position for text to draw in device independent units.
        * @param    text
        *           The new text to draw at the given position.
        */
       public static void drawTextAtXLeft(double x, String text)
       {
          resolveCurrentMetafile().drawTextAtX(x, text, "left");
       }

       /**
        * Draws the text at the given X position and current Y position and left alignment.
        *
        * @param    x
        *           The X position for text to draw in device independent units.
        * @param    text
        *           The new text to draw at the given position.
        */
       public static void drawTextAtXLeft(decimal x, character text)
       {
          if (isParameterValid(x) && isParameterValid(text))
          {
             drawTextAtXLeft(x.doubleValue(), text.getValue());
          }
       }

       /**
        * Draws the text at the given X position and current Y position and left alignment.
        *
        * @param    x
        *           The X position for text to draw in device independent units.
        * @param    text
        *           The new text to draw at the given position.
        */
       public static void drawTextAtXRight(double x, String text)
       {
          resolveCurrentMetafile().drawTextAtX(x, text, "right");
       }

       /**
        * Draws the text at the given X position and current Y position and left alignment.
        *
        * @param    x
        *           The X position for text to draw in device independent units.
        * @param    text
        *           The new text to draw at the given position.
        */
       public static void drawTextAtXRight(decimal x, character text)
       {
          if (isParameterValid(x) && isParameterValid(text))
          {
             drawTextAtXRight(x.doubleValue(), text.getValue());
          }
       }

       /**
        * Draws the text between left and right X positions and current Y position with given
        * alignment.  If text does not fit the area it will be truncated until match.
        *
        * @param    left
        *           The left X position for text to draw in device independent units.
        * @param    right
        *           The right X position for text to draw in device independent units.
        * @param    text
        *           The new text to fit and draw in the given positions.
        * @param    align
        *           The alignment value, either left or right.
        */
       public static void drawTextBetween(double left, double right, String text, String align)
       {
          resolveCurrentMetafile().drawTextBetween(left, right, text, align);
       }

       /**
        * Draws the text between left and right X positions and current Y position with given
        * alignment.  If text does not fit the area it will be truncated until match.
        *
        * @param    left
        *           The left X position for text to draw in device independent units.
        * @param    right
        *           The right X position for text to draw in device independent units.
        * @param    text
        *           The new text to fit and draw in the given positions.
        * @param    align
        *           The alignment value, either left or right.
        */
       public static void drawTextBetween(decimal left, decimal right, character text, String align)
       {
          if (isParameterValid(left) && isParameterValid(right) && isParameterValid(text))
          {
             drawTextBetween(left.doubleValue(), right.doubleValue(), text.getValue(), align);
          }
       }

       /**
        * Draws the text between left and right X positions and current Y position with left
        * alignment.  If text does not fit the area it will be truncated until match.
        *
        * @param    left
        *           The left X position for text to draw in device independent units.
        * @param    right
        *           The right X position for text to draw in device independent units.
        * @param    text
        *           The new text to fit and draw in the given positions.
        */
       public static void drawTextBetweenLeft(double left, double right, String text)
       {
          resolveCurrentMetafile().drawTextBetween(left, right, text, "left");
       }

       /**
        * Draws the text between left and right X positions and current Y position with left
        * alignment.  If text does not fit the area it will be truncated until match.
        *
        * @param    left
        *           The left X position for text to draw in device independent units.
        * @param    right
        *           The right X position for text to draw in device independent units.
        * @param    text
        *           The new text to fit and draw in the given positions.
        */
       public static void drawTextBetweenLeft(decimal left, decimal right, character text)
       {
          if (isParameterValid(left) && isParameterValid(right) && isParameterValid(text))
          {
             drawTextBetweenLeft(left.doubleValue(), right.doubleValue(), text.getValue());
          }
       }

       /**
        * Draws the text between left and right X positions and current Y position with right
        * alignment.  If text does not fit the area it will be truncated until match.
        *
        * @param    left
        *           The left X position for text to draw in device independent units.
        * @param    right
        *           The right X position for text to draw in device independent units.
        * @param    text
        *           The new text to fit and draw in the given positions.
        */
       public static void drawTextBetweenRight(double left, double right, String text)
       {
          resolveCurrentMetafile().drawTextBetween(left, right, text, "right");
       }

       /**
        * Draws the text between left and right X positions and current Y position with right
        * alignment.  If text does not fit the area it will be truncated until match.
        *
        * @param    left
        *           The left X position for text to draw in device independent units.
        * @param    right
        *           The right X position for text to draw in device independent units.
        * @param    text
        *           The new text to fit and draw in the given positions.
        */
       public static void drawTextBetweenRight(decimal left, decimal right, character text)
       {
          if (isParameterValid(left) && isParameterValid(right) && isParameterValid(text))
          {
             drawTextBetweenRight(left.doubleValue(), right.doubleValue(), text.getValue());
          }
       }

       /**
        * Sets new values for line attributes.
        *
        * @param    style
        *           The new String value for the current line style.  The valid style values:
        *           "solid", "dash", "dot", "dashdot", "dashdotdot", "null", "insideframe".
        * @param    thickness
        *           The thick of the line in device independent units.
        * @param    redColor
        *           The new red part of the color value.
        * @param    greenColor
        *           The new green part of the color value.
        * @param    blueColor
        *           The new blue part of the color value.
        */
       public static void setLineAttributes(String style, double thickness,
                                            int redColor, int greenColor, int blueColor)
       {
          resolveCurrentMetafile().setLineAttributes(style, thickness,
                                                     redColor, greenColor, blueColor);
       }

       /**
        * Sets new values for line attributes.
        *
        * @param    style
        *           The new String value for the current line style.  The valid style values:
        *           "solid", "dash", "dot", "dashdot", "dashdotdot", "null", "insideframe".
        * @param    thickness
        *           The thick of the line in device independent units.
        * @param    redColor
        *           The new red part of the color value.
        * @param    greenColor
        *           The new green part of the color value.
        * @param    blueColor
        *           The new blue part of the color value.
        */
       public static void setLineAttributes(String style, decimal thickness,
                                            int redColor, int greenColor, int blueColor)
       {
          if (isParameterValid(thickness))
          {
             setLineAttributes(style, thickness.doubleValue(), redColor, greenColor, blueColor);
          }
       }

       /**
        * Sets new values for line attributes.
        *
        * @param    style
        *           The new String value for the current line style.  The valid style values:
        *           "solid", "dash", "dot", "dashdot", "dashdotdot", "null", "insideframe".
        * @param    thickness
        *           The thick of the line in device independent units.
        * @param    redColor
        *           The new red part of the color value.
        * @param    greenColor
        *           The new green part of the color value.
        * @param    blueColor
        *           The new blue part of the color value.
        */
       public static void setLineAttributes(character style, decimal thickness,
                                            integer redColor, integer greenColor, integer blueColor)
       {
          if (isParameterValid(style) && isParameterValid(thickness) && isParameterValid(redColor) &&
              isParameterValid(greenColor) && isParameterValid(blueColor))
          {
             setLineAttributes(style.getValue(), thickness.doubleValue(), redColor.intValue(),
                               greenColor.intValue(), blueColor.intValue());
          }
       }

       /**
        * Sets new current color value to be used in line based primitives.
        *
        * @param    rgbColor
        *           The new 32-bit color value packed in int value.
        */
       public static void setLineColor(int rgbColor)
       {
          resolveCurrentMetafile().setLineColor(rgbColor);
       }

       /**
        * Sets new current color value to be used in line based primitives.
        *
        * @param    rgbColor
        *           The new 32-bit color value packed in int value.
        */
       public static void setLineColor(integer rgbColor)
       {
          if (isParameterValid(rgbColor))
          {
             setLineColor(rgbColor.intValue());
          }
       }

       /**
        * Sets new values for line attributes.
        *
        * @param    style
        *           The new String value for the current line style.  The valid style values:
        *           "solid", "dash", "dot", "dashdot", "dashdotdot", "null", "insideframe".
        * @param    thickness
        *           The thick of the line in device independent units.
        */
       public static void setLineStyle(String style, double thickness)
       {
          resolveCurrentMetafile().setLineStyle(style, thickness);
       }

       /**
        * Sets new values for line attributes.
        *
        * @param    style
        *           The new String value for the current line style.  The valid style values:
        *           "solid", "dash", "dot", "dashdot", "dashdotdot", "null", "insideframe".
        * @param    thickness
        *           The thick of the line in device independent units.
        */
       public static void setLineStyle(String style, decimal thickness)
       {
          if (isParameterValid(thickness))
          {
             setLineStyle(style, thickness.doubleValue());
          }
       }

       /**
        * Sets new values for line attributes.
        *
        * @param    style
        *           The new String value for the current line style.  The valid style values:
        *           "solid", "dash", "dot", "dashdot", "dashdotdot", "null", "insideframe".
        * @param    thickness
        *           The thick of the line in device independent units.
        */
       public static void setLineStyle(character style, decimal thickness)
       {
          if (isParameterValid(style) && isParameterValid(thickness))
          {
             setLineStyle(style.getValue(), thickness.doubleValue());
          }
       }

       /**
        * Sets new current color value to be used in fill based primitives.
        *
        * @param    rgbColor
        *           The new 32-bit color value packed in int value.
        */
       public static void setFillColor(int rgbColor)
       {
          resolveCurrentMetafile().setFillColor(rgbColor);
       }

       /**
        * Sets new current color value to be used in fill based primitives.
        *
        * @param    rgbColor
        *           The new 32-bit color value packed in int value.
        */
       public static void setFillColor(integer rgbColor)
       {
          if (isParameterValid(rgbColor))
          {
             setFillColor(rgbColor.intValue());
          }
       }

       /**
        * Draws the line from left-top point to right-bottom with current line attributes.
        * The coordintes are in device independent units.
        *
        * @param    left
        *           The left X position of the line start.
        * @param    top
        *           The top Y position of the line start.
        * @param    right
        *           The right X position of the line start.
        * @param    bottom
        *           The bottom Y position of the line start.
        */
       public static void drawLine(double left, double top, double right, double bottom)
       {
          resolveCurrentMetafile().drawLine(left, top, right, bottom);
       }

       /**
        * Draws the line from left-top point to right-bottom with current line attributes.
        * The coordintes are in device independent units.
        *
        * @param    left
        *           The left X position of the line start.
        * @param    top
        *           The top Y position of the line start.
        * @param    right
        *           The right X position of the line start.
        * @param    bottom
        *           The bottom Y position of the line start.
        */
       public static void drawLine(decimal left, decimal top, decimal right, decimal bottom)
       {
          if (isParameterValid(left) && isParameterValid(top) &&
              isParameterValid(right) && isParameterValid(bottom))
          {
             drawLine(left.doubleValue(), top.doubleValue(),
                      right.doubleValue(), bottom.doubleValue());
          }
       }

       /**
        * Draws the line from left-top point to right-bottom with current line attributes.
        * The coordintes are in device independent units.
        *
        * @param    left
        *           The left X position of the line start.
        * @param    top
        *           The top Y position of the line start.
        * @param    right
        *           The right X position of the line start.
        * @param    bottom
        *           The bottom Y position of the line start.
        */
       public static void drawLine(double left, decimal top, decimal right, decimal bottom)
       {
          if (isParameterValid(top) && isParameterValid(right) && isParameterValid(bottom))
          {
             drawLine(left, top.doubleValue(), right.doubleValue(), bottom.doubleValue());
          }
       }

       /**
        * Draws the line from left-top point to right-bottom with current line attributes.
        * The coordintes are in device independent units.
        *
        * @param    left
        *           The left X position of the line start.
        * @param    top
        *           The top Y position of the line start.
        * @param    right
        *           The right X position of the line start.
        * @param    bottom
        *           The bottom Y position of the line start.
        */
       public static void drawLine(decimal left, decimal top, double right, decimal bottom)
       {
          if (isParameterValid(left) && isParameterValid(top) && isParameterValid(bottom))
          {
             drawLine(left.doubleValue(), top.doubleValue(), right, bottom.doubleValue());
          }
       }

       /**
        * Draws the line from left-top point to right-bottom with current line attributes.
        * The coordintes are in device independent units.
        *
        * @param    left
        *           The left X position of the line start.
        * @param    top
        *           The top Y position of the line start.
        * @param    right
        *           The right X position of the line start.
        * @param    bottom
        *           The bottom Y position of the line start.
        */
       public static void drawLine(double left, decimal top, double right, decimal bottom)
       {
          if (isParameterValid(top) && isParameterValid(bottom))
          {
             drawLine(left, top.doubleValue(), right, bottom.doubleValue());
          }
       }

       /**
        * Draws the line from left-top point to right-bottom with current line attributes.
        * The coordintes are in device independent units.
        *
        * @param    left
        *           The left X position of the line start.
        * @param    top
        *           The top Y position of the line start.
        * @param    right
        *           The right X position of the line start.
        * @param    bottom
        *           The bottom Y position of the line start.
        */
       public static void drawLine(decimal left, double top, double right, double bottom)
       {
          if (isParameterValid(left))
          {
             drawLine(left.doubleValue(), top, right, bottom);
          }
       }

       /**
        * Draws the line from left-top point to right-bottom with current line attributes.
        * The coordintes are in device independent units.
        *
        * @param    left
        *           The left X position of the line start.
        * @param    top
        *           The top Y position of the line start.
        * @param    right
        *           The right X position of the line start.
        * @param    bottom
        *           The bottom Y position of the line start.
        */
       public static void drawLine(decimal left, double top, decimal right, double bottom)
       {
          if (isParameterValid(left) && isParameterValid(right))
          {
             drawLine(left.doubleValue(), top, right.doubleValue(), bottom);
          }
       }

       /**
        * Draws the framed rectangle between left-top point and right-bottom with current line
        * attributes.  The coordintes are in device independent units.
        *
        * @param    left
        *           The left X position of the rectangle.
        * @param    top
        *           The top Y position of the rectangle.
        * @param    right
        *           The right X position of the rectangle.
        * @param    bottom
        *           The bottom Y position of the rectangle.
        */
       public static void drawRect(double left, double top, double right, double bottom)
       {
          resolveCurrentMetafile().drawRect(left, top, right, bottom);
       }

       /**
        * Draws the framed rectangle between left-top point and right-bottom with current line
        * attributes.  The coordintes are in device independent units.
        *
        * @param    left
        *           The left X position of the rectangle.
        * @param    top
        *           The top Y position of the rectangle.
        * @param    right
        *           The right X position of the rectangle.
        * @param    bottom
        *           The bottom Y position of the rectangle.
        */
       public static void drawRect(decimal left, decimal top, decimal right, decimal bottom)
       {
          if (isParameterValid(left) && isParameterValid(top) &&
              isParameterValid(right) && isParameterValid(bottom))
          {
             drawRect(left.doubleValue(), top.doubleValue(),
                      right.doubleValue(), bottom.doubleValue());
          }
       }

       /**
        * Draws the framed rectangle between left-top point and right-bottom with current line
        * attributes.  The coordintes are in device independent units.
        *
        * @param    left
        *           The left X position of the rectangle.
        * @param    top
        *           The top Y position of the rectangle.
        * @param    right
        *           The right X position of the rectangle.
        * @param    bottom
        *           The bottom Y position of the rectangle.
        */
       public static void drawRect(double left, decimal top, decimal right, decimal bottom)
       {
          if (isParameterValid(top) && isParameterValid(right) && isParameterValid(bottom))
          {
             drawRect(left, top.doubleValue(), right.doubleValue(), bottom.doubleValue());
          }
       }

       /**
        * Draws the rectangle between left-top point and right-bottom with current line and fill
        * attributes.  The coordintes are in device independent units.
        *
        * @param    left
        *           The left X position of the rectangle.
        * @param    top
        *           The top Y position of the rectangle.
        * @param    right
        *           The right X position of the rectangle.
        * @param    bottom
        *           The bottom Y position of the rectangle.
        */
       public static void drawFilledRect(double left, double top, double right, double bottom)
       {
          resolveCurrentMetafile().drawFilledRect(left, top, right, bottom);
       }

       /**
        * Draws the rectangle between left-top point and right-bottom with current line and fill
        * attributes.  The coordintes are in device independent units.
        *
        * @param    left
        *           The left X position of the rectangle.
        * @param    top
        *           The top Y position of the rectangle.
        * @param    right
        *           The right X position of the rectangle.
        * @param    bottom
        *           The bottom Y position of the rectangle.
        */
       public static void drawFilledRect(decimal left, decimal top, decimal right, decimal bottom)
       {
          if (isParameterValid(left) && isParameterValid(top) &&
              isParameterValid(right) && isParameterValid(bottom))
          {
             drawFilledRect(left.doubleValue(), top.doubleValue(),
                            right.doubleValue(), bottom.doubleValue());
          }
       }

       /**
        * Sets new values for image alignment.
        *
        * @param    horizAlign
        *           The new String value for horizontal text alignment.  The valid alignment values:
        *           "left", "center", "right".
        * @param    vertAlign
        *           The new String value for vertical text alignment.  The valid alignment values:
        *           "top", "baseline", "bottom".
        */
       public static void setImageAlign(String horizAlign, String vertAlign)
       {
          resolveCurrentMetafile().setImageAlign(horizAlign, vertAlign);
       }

       /**
        * Draws the image.
        *
        * @param    option
        *           The image painting option for example "origratio=yes".
        * @param    x
        *           The image X position.
        * @param    y
        *           The image Y position.
        * @param    width
        *           The image width in metric units.
        * @param    height
        *           The image height in metric units.
        * @param    filename
        *           The name of the file to use as image.
        * @param    imgWidth
        *           The final painted image width in metric units(return value).
        * @param    imgHeight
        *           The final painted image height in metric units(return value).
        */
       public static void drawImage(String option, double x, double y, double width, double height,
                                    String filename, decimal imgWidth, decimal imgHeight)
       {
          resolveCurrentMetafile().drawImage(option, x, y, width, height, filename,
                                             imgWidth, imgHeight);
       }

       /**
        * Draws the image.
        *
        * @param    option
        *           The image painting option for example "origratio=yes".
        * @param    x
        *           The image X position.
        * @param    y
        *           The image Y position.
        * @param    width
        *           The image width in metric units.
        * @param    height
        *           The image height in metric units.
        * @param    filename
        *           The name of the file to use as image.
        * @param    imgWidth
        *           The final painted image width in metric units(return value).
        * @param    imgHeight
        *           The final painted image height in metric units(return value).
        */
       public static void drawImage(String option, decimal x, decimal y, decimal width,
                                    decimal height, character filename,
                                    decimal imgWidth, decimal imgHeight)
       {
          if (isParameterValid(x) && isParameterValid(y) && isParameterValid(width) &&
              isParameterValid(height) && isParameterValid(filename))
          {
             drawImage(option, x.doubleValue(), y.doubleValue(), width.doubleValue(),
                       height.doubleValue(), filename.getValue(), imgWidth, imgHeight);
          }
       }

       /**
        * Draws the image.
        *
        * @param    option
        *           The image painting option for example "origratio=yes".
        * @param    x
        *           The image X position.
        * @param    y
        *           The image Y position.
        * @param    width
        *           The image width in metric units.
        * @param    height
        *           The image height in metric units.
        * @param    filename
        *           The name of the file to use as image.
        * @param    imgWidth
        *           The final painted image width in metric units(return value).
        * @param    imgHeight
        *           The final painted image height in metric units(return value).
        */
       public static void drawImage(character option, decimal x, decimal y, decimal width,
                                    decimal height, character filename,
                                    decimal imgWidth, decimal imgHeight)
       {
          if (isParameterValid(option) && isParameterValid(x) && isParameterValid(y) &&
               isParameterValid(width) && isParameterValid(height) && isParameterValid(filename))
          {
             drawImage(option.getValue(), x.doubleValue(), y.doubleValue(), width.doubleValue(),
                       height.doubleValue(), filename.getValue(), imgWidth, imgHeight);
          }
       }

       /**
        * Draws the image.
        *
        * @param    option
        *           The image painting option for example "origratio=yes".
        * @param    x
        *           The image X position.
        * @param    y
        *           The image Y position.
        * @param    width
        *           The image width in metric units.
        * @param    height
        *           The image height in metric units.
        * @param    filename
        *           The name of the file to use as image.
        * @param    imgWidth
        *           The final painted image width in metric units(return value).
        * @param    imgHeight
        *           The final painted image height in metric units(return value).
        */
       public static void drawImage(String option, decimal x, decimal y, double width, double height,
                                    character filename, decimal imgWidth, decimal imgHeight)
       {
          if (isParameterValid(x) && isParameterValid(y) && isParameterValid(filename))
          {
             drawImage(option, x.doubleValue(), y.doubleValue(), width, height, filename.getValue(),
                       imgWidth, imgHeight);
          }
       }

       /**
        * Draws the part of the image.
        *
        * @param    x
        *           The image X position.
        * @param    y
        *           The image Y position.
        * @param    widthPix
        *           The image width in pixels.
        * @param    heightPix
        *           The image height in pixels.
        * @param    maxWidthCm
        *           The image maximum right border not to exceed.
        * @param    filename
        *           The name of the file to use as image.
        */
       public static void drawImagePart(double x, double y, int widthPix, int heightPix,
                                        double maxWidthCm, String filename)
       {
          resolveCurrentMetafile().drawImagePart(x, y, widthPix, heightPix, maxWidthCm, filename);
       }

       /**
        * Draws the part of the image.
        *
        * @param    x
        *           The image X position.
        * @param    y
        *           The image Y position.
        * @param    widthPix
        *           The image width in pixels.
        * @param    heightPix
        *           The image height in pixels.
        * @param    maxWidthCm
        *           The image maximum right border not to exceed.
        * @param    filename
        *           The name of the file to use as image.
        */
       public static void drawImagePart(decimal x, decimal y, integer widthPix, integer heightPix,
                                        decimal maxWidthCm, character filename)
       {
          if (isParameterValid(x) && isParameterValid(y) && isParameterValid(widthPix) &&
              isParameterValid(heightPix) && isParameterValid(maxWidthCm) &&
              isParameterValid(filename))
          {
             drawImagePart(x.doubleValue(), y.doubleValue(), widthPix.intValue(),
                           heightPix.intValue(), maxWidthCm.doubleValue(), filename.getValue());
          }
       }

       /**
        * Sets new value for current page orientation.
        *
        * @param    mode
        *           The new String value for page orientation.  The valid alignment values:
        *           "standard", "portrait", "landscape".
        */
       public static void setPageOrientation(character mode)
       {
          if (isParameterValid(mode))
          {
             resolveCurrentMetafile().setPageOrientation(mode.getValue());
          }
       }

       /**
        * Sets new value for current page orientation.
        *
        * @param    mode
        *           The new String value for page orientation.  The valid alignment values:
        *           "standard", "portrait", "landscape".
        */
       public static void setPageOrientation(String mode)
       {
          resolveCurrentMetafile().setPageOrientation(mode);
       }

       /**
        * Gets the current page width.
        *
        * @param    pageWidth
        *           The current page width value in device independent units.
        */
       public static void getPageWidth(decimal pageWidth)
       {
          resolveCurrentMetafile().getPageWidth(pageWidth);
       }

       /**
        * Gets the current page height.
        *
        * @param    pageHeight
        *           The current page height value in device independent units.
        */
       public static void getPageHeight(decimal pageHeight)
       {
          resolveCurrentMetafile().getPageHeight(pageHeight);
       }

       /**
        * Sets new value for current/maximum page number.
        *
        * @param    mode
        *           The new String value for page mode.  The valid values: "append", "new".
        * @param    newMaxPageNum
        *           The integer max page number value.
        */
       public static void resetPage(String mode, int newMaxPageNum)
       {
          resolveCurrentMetafile().resetPage(mode, newMaxPageNum);
       }

       /**
        * Sets new value for current/maximum page number.
        *
        * @param    mode
        *           The new String value for page mode.  The valid values: "append", "new".
        * @param    newMaxPageNum
        *           The integer max page number value.
        */
       public static void resetPage(String mode, integer newMaxPageNum)
       {
          if (isParameterValid(newMaxPageNum))
          {
             resetPage(mode, newMaxPageNum.intValue());
          }
       }

       /**
        * Gets the current page number.
        *
        * @param    pageNum
        *           The current integer page number value.
        */
       public static void getPageNumber(integer pageNum)
       {
          resolveCurrentMetafile().getPageNumber(pageNum);
       }

       /**
        * Sets new value for current page number.
        *
        * @param    newPageNum
        *           The integer page number value.
        */
       public static void setPageNumber(int newPageNum)
       {
          resolveCurrentMetafile().setPageNumber(newPageNum);
       }

       /**
        * Sets the new current position for page number.  Can be used to draw page number text for
        * header and footer.
        *
        * @param    x
        *           The new X position for page nummber.
        * @param    y
        *           The new Y position for page nummber.
        */
       public static void setPageNumberPosition(double x, double y)
       {
          resolveCurrentMetafile().setPageNumberPosition(x, y);
       }

       /**
        * Sets the new current position for page number.  Can be used to draw page number text for
        * header and footer.
        *
        * @param    x
        *           The new X position for page nummber.
        * @param    y
        *           The new Y position for page nummber.
        */
       public static void setPageNumberPosition(decimal x, decimal y)
       {
          if (isParameterValid(x) && isParameterValid(y))
          {
             setPageNumberPosition(x.doubleValue(), y.doubleValue());
          }
       }

       /**
        * Sets new value for current page number text.
        *
        * @param    text
        *           The new text for page number.
        * @param    pgNumberNdx
        *           The index of new text for page number.
        */
       public static void setPageNumberText(String text, int pgNumberNdx)
       {
          resolveCurrentMetafile().setPageNumberText(text, pgNumberNdx);
       }

       /**
        * Sets new value for current page number text.
        *
        * @param    text
        *           The new text for page number.
        * @param    pgNumberNdx
        *           The index of new text for page number.
        */
       public static void setPageNumberText(character text, int pgNumberNdx)
       {
          if (isParameterValid(text))
          {
             setPageNumberText(text.getValue(), pgNumberNdx);
          }
       }

       /**
        * Sets new value for current page number text(alternative version).
        *
        * @param    x
        *           The new X position for page nummber.
        * @param    y
        *           The new Y position for page nummber.
        * @param    text
        *           The new text for page number.
        */
       public static void setPageNumberText(double x, double y, String text)
       {
          resolveCurrentMetafile().setPageNumberPosition(x, y);
          resolveCurrentMetafile().setPageNumberText(text, 1);
       }

       /**
        * Sets new value for current page number text(alternative version).
        *
        * @param    x
        *           The new X position for page nummber.
        * @param    y
        *           The new Y position for page nummber.
        * @param    text
        *           The new text for page number.
        */
       public static void setPageNumberText(decimal x, decimal y, character text)
       {
          if (isParameterValid(x) && isParameterValid(y) && isParameterValid(text))
          {
             setPageNumberText(x.doubleValue(), y.doubleValue(), text.getValue());
          }
       }

       /**
        * Sets to show page footer or not.
        *
        * @param    show
        *           The <code>1</code> value means show footer, <code>0</code> means no footer.
        */
       public static void showPageFooter(int show)
       {
          resolveCurrentMetafile().showPageFooter(show == 1);
       }

       /**
        * Sets to show page footer or not.
        *
        * @param    show
        *           The <code>1</code> value means show footer, <code>0</code> means no footer.
        */
       public static void showPageFooter(integer show)
       {
          if (isParameterValid(show))
          {
             showPageFooter(show.intValue());
          }
       }

       /**
        * General metafile subsystem init procedure.
        *
        * @param    footerTextHeight
        *           The height of the footed text in device independent units.
        * @param    tmpDir
        *           The temporary directory to use.
        * @param    ok
        *           The return value, meaning success init or not.
        */
       public static void initialize(double footerTextHeight, String tmpDir, integer ok)
       {
          resolveCurrentMetafile().initialize(footerTextHeight, tmpDir, ok);
       }

       /**
        * General metafile subsystem init procedure.
        *
        * @param    footerTextHeight
        *           The height of the footed text in device independent units.
        * @param    tmpDir
        *           The temporary directory to use.
        * @param    ok
        *           The return value, meaning success init or not.
        */
       public static void initialize(decimal footerTextHeight, character tmpDir, integer ok)
       {
          if (isParameterValid(footerTextHeight) && isParameterValid(tmpDir))
          {
             initialize(footerTextHeight.doubleValue(), tmpDir.getValue(), ok);
          }
       }

       /**
        * Gets optionally set temporary directory to store generated PDF temporary files.
        *
        * @return   The currently used temporary directory.
        */
       public static String getTmpDirectory()
       {
          return resolveCurrentMetafile().getTmpDirectory();
       }

       /**
        * Makes PDF file from the currently recorded metafile.
        *
        * @param    pdfFileName
        *           The name of the PDF file to create.
        */
       public static void makePdf(String pdfFileName)
       {
          // get the metafile object
          WorkArea wa = work.obtain();

          // intercept Jasper related issues tp avoid client abend too
          try
          {
             // ensure all pending objects are committed
             wa.currentMf.stopRecording();

             // time to create jasper related worker
             Xpr2PdfWorker pdfOutput = new Xpr2PdfWorker(wa.currentMf, pdfFileName);
             // export to PDF
             if (!pdfOutput.export(false))
             {
                XprHelper.displayOrLogError(String.format("MetafileHelper export failed for %s.",
                                                          pdfFileName));
             }
          }
          catch (Exception e)
          {
             XprHelper.displayOrLogError(String.format(
                "MetafileHelper has faced unexpected problem with report generation for PDF file %s.",
                 pdfFileName), e);
          }

          // after pdf was made we need to clean up current session based metafile
          wa.currentMf = null;

          // as final step we load file into client context
          if(openFile)
          {
             WebBrowserManager.openMimeResource(XprHelper.PDF_MIME,
                                                XprHelper.FILE_PROTOCOL + pdfFileName, false);
          }
       }

       /**
        * Makes PDF file from the currently recorded metafile.
        *
        * @param    pdfFileName
        *           The name of the PDF file to create.
        */
       public static void makePdf(character pdfFileName)
       {
          if (isParameterValid(pdfFileName))
          {
             makePdf(pdfFileName.getValue());
          }
       }

       /**
        * Makes PDF file from the currently recorded metafile with preview mode or not.
        *
        * @param    pdfFileName
        *           The name of the PDF file to create.
        * @param    openFilePDF
        *           preview prevents opening the PDF file instantly
        */
       public static void makePdf(character pdfFileName, boolean openFilePDF)
       {
          if (isParameterValid(pdfFileName))
          {
             makePdf(pdfFileName.getValue(), openFilePDF);
          }
       }

       /**
        * Makes PDF file from the currently recorded metafile with preview mode or not.
        *
        * @param    pdfFileName
        *           The name of the PDF file to create.
        * @param    openFilePDF
        *           preview prevents opening the PDF file instantly
        */
       public static void makePdf(String pdfFileName, logical openFilePDF)
       {
          if (isParameterValid(openFilePDF))
          {
             makePdf(pdfFileName, openFilePDF.booleanValue());
          }
       }

       /**
        * Makes PDF file from the currently recorded metafile with preview mode or not.
        *
        * @param    pdfFileName
        *           The name of the PDF file to create.
        * @param    openFilePDF
        *           preview prevents opening the PDF file instantly
        */
       public static void makePdf(character pdfFileName, logical openFilePDF)
       {
          if (isParameterValid(pdfFileName) && isParameterValid(openFilePDF))
          {
             makePdf(pdfFileName.getValue(), openFilePDF.booleanValue());
          }
       }

       /**
        * Makes PDF file from the currently recorded metafile with preview mode or not.
        *
        * @param    pdfFileName
        *           The name of the PDF file to create.
        * @param    openFilePDF
        *           preview prevents opening the PDF file instantly
        */
       public static void makePdf(String pdfFileName, boolean openFilePDF)
       {
          try
          {
             openFile = openFilePDF;
             makePdf(pdfFileName);
          }
          finally
          {
             openFile = true;
          }
       }

       /**
        * Stops all operation for the current metafile.
        */
       public static void stopRecording()
       {
          resolveCurrentMetafile().stopRecording();
       }

       /**
        * Interrupts all operation for the current metafile.
        */
       public static void interruptRecording()
       {
          work.obtain().currentMf = null;
       }

       /**
        * Converts X cm metafile coordinate to XPR column value.
        *
        * @param    x
        *           The X coordinate to transform.
        *
        * @return   The column value to use in JasperReports XPR objects handling.
        */
       public static double x2Col(double x)
       {
          // gets the pixel value of the metafile coord and converts to XPR column
          return (double)mfUnits2pix(x) / pixPerCol;
       }

       /**
        * Converts Y cm metafile coordinate to XPR row value.
        *
        * @param    y
        *           The Y coordinate to transform.
        *
        * @return   The row value to use in JasperReports XPR objects handling.
        */
       public static double y2Row(double y)
       {
          // gets the pixel value of the metafile coord and converts to XPR row
          return (double)mfUnits2pix(y) / pixPerRow;
       }

       /**
        * Converts pixel coordinate value into metafile device independent unit value.
        *
        * @param    pixelValue
        *           The coordinate in pixels to transform.
        * @param    mfUnits
        *           The device independent unit, can be inch/cm/mm.
        */
       public static void pix2MfUnits(int pixelValue, decimal mfUnits)
       {
          double val = pix2MfUnits(pixelValue);

          mfUnits.assign(new decimal(val));
       }

       /**
        * Converts pixel coordinate value into metafile device independent unit value.
        *
        * @param    pixelValue
        *           The coordinate in pixels to transform.
        * @param    mfUnits
        *           The device independent unit, can be inch/cm/mm.
        */
       public static void pix2MfUnits(integer pixelValue, decimal mfUnits)
       {
          if (isParameterValid(pixelValue))
          {
             pix2MfUnits(pixelValue.intValue(), mfUnits);
          }
       }

       /**
        * Converts pixel coordinate value into metafile device independent unit value.
        *
        * @param    pixelValue
        *           The coordinate in pixels to transform.
        *
        * @return   The device independent unit, can be inch/cm/mm.
        */
       public static double pix2MfUnits(float pixelValue)
       {
          // defaulting to inches
          double res = (double)pixelValue / (double) (Xpr2PdfWorker.DPI_DEFAULT);

          // can be CM or MM as well
          if (units == UNITS_MM)
          {
             res *= XprEntity.INCH_2_MM;
          }
          else if (units == UNITS_CM)
          {
             res *= XprEntity.INCH_2_MM / 10.0;
          }

          return res;
       }

       /**
        * Converts metafile coordinate value into pixel based coordinate.
        *
        * @param    mfValue
        *           The coordinate in metafile units to transform.
        *
        * @return   The coordinale in pixels.
        */
       public static int mfUnits2pix(double mfValue)
       {
          // defaulting to use inches
          double res = (double)mfValue * (double) (Xpr2PdfWorker.DPI_DEFAULT);

          // can be CM or MM as well
          if (units == UNITS_MM)
          {
             res /= XprEntity.INCH_2_MM;
          }
          else if (units == UNITS_CM)
          {
             res /= XprEntity.INCH_2_MM / 10.0;
          }

          return (int)Math.round(res);
       }

       /**
        * Checks if the given parameter is valid and can be used in Metafile api.
        *
        * @param    op
        *           The parameter to test.
        *
        * @return   <code>TRUE</code> if valid, <code>FALSE</code> otherwise.
        */
       public static boolean isParameterValid(BaseDataType op)
       {
          return op != null && !op.isUnknown();
       }

       /**
        * Calculates the scaling factor for given text.  Different letters have different width for variable
        * size fonts like Arial.  We need to compensate this difference to get proper text width.
        *
        * @param    textToTest
        *           The text to measure letters scale.
        * @param    fontName
        *           The font to be used to draw the text.
        * @param    isBold
        *           If the font to be used is bold or not.
        * @param    isItalic
        *           If the font to be used is italic or not.
        *
        * @return   The scaling for given string to calculate text width.
        */
       public static double getTextWeightScale(String textToTest, String fontName,
                                               boolean isBold, boolean isItalic)
       {
          double res = 0.0;

          // some simple NPE and errors protection
          // fixed width font can also be exculded
          if (textToTest == null || textToTest.isEmpty() || fontName.equals("Courier"))
          {
             return 1.0;
          }

          // text length
          int length = textToTest.length();
          for (int i = 0; i < length; i++)
          {
             char chNext = textToTest.charAt(i);
             switch (chNext)
             {
                case ' ':
                case ':':
                case '.':
                case ',':
                case ';':
                case '[':
                case ']':
                case '(':
                case ')':
                case '!':
                case '\'':
                case '|':
                {
                   // very narrow letters
                   res += 0.555;
                   break;
                }
                case 'I':
                case 'i':
                case 'L':
                case 'l':
                case '/':
                case '\\':
                case '-':
                case '*':
                case 'f':
                case 't':
                case '{':
                case '}':
                {
                   // narrow letters
                   res += 0.75;
                   break;
                }
                case 'Z':
                case 'M':
                case 'G':
                case 'E':
                case 'R':
                case 'O':
                case 'D':
                case 'N':
                case 'S':
                {
                   // very wide letters
                   res += 1.35;
                   break;
                }
                case 'W':
                case '@':
                case '%':
                {
                   // extra wide letters
                   res += 1.55;
                   break;
                }
                case 'A':
                case 'V':
                case 'P':
                case 'C':
                case 'w':
                case 'Q':
                {
                   // wide letters
                   res += 1.15;
                   break;
                }
                default:
                {
                   // all other letters are regular
                   res += 1.0;
                   break;
                }
             }
             // bold and possibly italic fonts need additional corrections in certain cases
             if (isBold && !Character.isDigit(chNext))
             {
                res += 0.1;
             }
             else if (isItalic)
             {
                res += 0.0;
             }
          }
          // normalize the scale factor
          res /= (double)length;

          // the Arial font needs to be a bit smaller comparing to all others
          return fontName.equals("Arial") ? res * 0.85 : res;
       }

       /**
        * Gets the instance of the current metafile to process.
        *
        * @return   The current metafile.
        */
       private static Metafile resolveCurrentMetafile()
       {
          WorkArea wa = work.obtain();

          if (wa.currentMf == null)
          {
             wa.currentMf = new Metafile();
          }

          return wa.currentMf;
       }

       /**
        * Stores global data relating to the state of the current context.
        */
       private static class WorkArea
       {
          /** Current per session context metafile instance. */
          private Metafile currentMf = null;
       }

       /**
        * Simple container that stores and returns a context-local instance of
        * the global work area.
        */
       private static class ContextContainer
       extends ContextLocal<WorkArea>
       {
          /**
           * Obtains the context-local instance of the contained global work
           * area.
           *
           * @return   The work area associated with this context.
           */
          public WorkArea obtain()
          {
             return this.get();
          }

          /**
           * Initializes the work area, the first time it is requested within a
           * new context.
           *
           * @return   The newly instantiated work area.
           */
          protected synchronized WorkArea initialValue()
          {
             WorkArea wa = new WorkArea();

             return wa;
          }
       }
    }