/*

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package org.apache.batik.transcoder.wmf.tosvg;

import java.awt.Color;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import org.apache.batik.ext.awt.geom.Polygon2D;
import org.apache.batik.ext.awt.geom.Polyline2D;
import org.apache.batik.transcoder.wmf.WMFConstants;

/**
 * This class holds simple properties about a WMF Metafile. It can be used
 * whenever general information must be retrieved about this file.
 *
 * @version $Id: WMFHeaderProperties.java 1831630 2018-05-15 12:56:55Z ssteiner $
 */
public class WMFHeaderProperties extends AbstractWMFReader {

    private static final Integer INTEGER_0 = 0;

    protected DataInputStream stream;
    private int _bleft, _bright, _btop, _bbottom, _bwidth, _bheight;
    private int _ileft, _iright, _itop, _ibottom;
    private float scale = 1.0f;
    private int startX = 0;
    private int startY = 0;
    private int currentHorizAlign = 0;
    private int currentVertAlign = 0;
    private WMFFont wf = null;
    private static final FontRenderContext fontCtx =
            new FontRenderContext(new AffineTransform(), false, true);
    private transient boolean firstEffectivePaint = true;
    public static final int PEN = 1;
    public static final int BRUSH = 2;
    public static final int FONT = 3;
    public static final int NULL_PEN = 4;
    public static final int NULL_BRUSH = 5;
    public static final int PALETTE = 6;
    public static final int OBJ_BITMAP = 7;
    public static final int OBJ_REGION = 8;

    /** Creates a new WMFHeaderProperties, and sets the associated WMF File.
     * @param wmffile the WMF Metafile
     */
    public WMFHeaderProperties(File wmffile) throws IOException {
        super();
        reset();
        stream = new DataInputStream(new BufferedInputStream(new FileInputStream(wmffile)));
        read(stream);
        stream.close();
    }

    /** Creates a new WMFHeaderProperties, with no associated file.
     */
    public WMFHeaderProperties() {
        super();
    }

    public void closeResource() {
        try {
            if (stream != null) stream.close();
            } catch (IOException e) {
        }
    }

    /** Creates the properties associated file.
     */
    public void setFile(File wmffile) throws IOException {
        stream = new DataInputStream(new BufferedInputStream(new FileInputStream(wmffile)));
        read(stream);
        stream.close();
    }

    /**
     * Resets the internal storage and viewport coordinates.
     */
    public void reset() {
        left = 0;
        right = 0;
        top = 1000;
        bottom = 1000;
        inch = 84;
        _bleft = -1;
        _bright = -1;
        _btop = -1;
        _bbottom = -1;
        _ileft = -1;
        _iright = -1;
        _itop = -1;
        _ibottom = -1;
        _bwidth = -1;
        _bheight= -1;
        vpW = -1;
        vpH = -1;
        vpX = 0;
        vpY = 0;
        startX = 0;
        startY = 0;
        scaleXY = 1f;        
        firstEffectivePaint = true;
    }

    /** Get the associated stream.
     */
    public DataInputStream getStream() {
        return stream;
    }

    protected boolean readRecords(DataInputStream is) throws IOException {
        // effective reading of the rest of the file
        short functionId = 1;
        int recSize = 0;
        int gdiIndex; // the last Object index
        int brushObject = -1; // the last brush
        int penObject = -1; // the last pen
        int fontObject = -1; // the last font
        GdiObject gdiObj;

        while (functionId > 0) {
            recSize = readInt( is );
            // Subtract size in 16-bit words of recSize and functionId;
            recSize -= 3;

            functionId = readShort( is );
            if ( functionId <= 0 )
            break;

            switch ( functionId ) {
            case WMFConstants.META_SETMAPMODE: {
                int mapmode = readShort( is );  
                // change isotropic if mode is anisotropic
                if (mapmode == WMFConstants.MM_ANISOTROPIC) isotropic = false;
            }
                break;                                                
            case WMFConstants.META_SETWINDOWORG: {
                vpY = readShort( is );
                vpX = readShort( is );
            }
                break;
            case WMFConstants.META_SETWINDOWEXT: {
                vpH = readShort( is );
                vpW = readShort( is );
                if (! isotropic) scaleXY = (float)vpW / vpH;
                vpW = (int)(vpW * scaleXY);                
            }
            break;

            case WMFConstants.META_CREATEPENINDIRECT:
                {
                    int objIndex = 0;
                    int penStyle = readShort( is );

                    readInt( is ); // width
                    // color definition
                    int colorref = readInt( is );
                    int red = colorref & 0xff;
                    int green = ( colorref & 0xff00 ) >> 8;
                    int blue = ( colorref & 0xff0000 ) >> 16;
                    Color color = new Color( red, green, blue);

                    if (recSize == 6) readShort(is); // if size greater than 5
                    if ( penStyle == WMFConstants.META_PS_NULL ) {
                        objIndex = addObjectAt( NULL_PEN, color, objIndex );
                    } else {
                        objIndex = addObjectAt( PEN, color, objIndex );
                    }
                }
                break;

            case WMFConstants.META_CREATEBRUSHINDIRECT:
                {
                    int objIndex = 0;
                    int brushStyle = readShort( is );
                    // color definition
                    int colorref = readInt( is );
                    int red = colorref & 0xff;
                    int green = ( colorref & 0xff00 ) >> 8;
                    int blue = ( colorref & 0xff0000 ) >> 16;
                    Color color = new Color( red, green, blue);

                    readShort( is ); // hatch
                    if ( brushStyle == WMFConstants.META_PS_NULL ) {
                        objIndex = addObjectAt( NULL_BRUSH, color, objIndex);
                    } else
                        objIndex = addObjectAt(BRUSH, color, objIndex );
                }
                break;

            case WMFConstants.META_SETTEXTALIGN:
                    int align = readShort( is );
                    // need to do this, because sometimes there is more than one short
                    if (recSize > 1) for (int i = 1; i < recSize; i++) readShort( is );
                    currentHorizAlign = WMFUtilities.getHorizontalAlignment(align);
                    currentVertAlign = WMFUtilities.getVerticalAlignment(align);
                    break;

            case WMFConstants.META_EXTTEXTOUT: {
                    int y = readShort( is );
                    int x = (int)(readShort( is ) * scaleXY);
                    int lenText = readShort( is );
                    int flag = readShort( is );
                    int read = 4; // used to track the actual size really read
                    boolean clipped = false;
                    int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
                    int len;
                    // determination of clipping property
                    if ((flag & WMFConstants.ETO_CLIPPED) != 0) {
                        x1 =  (int)(readShort( is ) * scaleXY);
                        y1 =  readShort( is );
                        x2 =  (int)(readShort( is ) * scaleXY);
                        y2 =  readShort( is );
                        read += 4;
                        clipped = true;
                    }
                    byte[] bstr = new byte[ lenText ];
                    int i = 0;
                    for ( ; i < lenText; i++ ) {
                        bstr[ i ] = is.readByte();
                    }
                    String sr = WMFUtilities.decodeString(wf, bstr);

                    read += (lenText + 1)/2;
                    /* must do this because WMF strings always have an even number of bytes, even
                     * if there is an odd number of characters
                     */
                    if (lenText % 2 != 0) is.readByte();
                    // if the record was not completely read, finish reading
                    if (read < recSize) for (int j = read; j < recSize; j++) readShort( is );
                    TextLayout layout = new TextLayout( sr, wf.font, fontCtx );

                    int lfWidth = (int)layout.getBounds().getWidth();
                    x = (int)layout.getBounds().getX();
                    int lfHeight =
                        (int)getVerticalAlignmentValue(layout, currentVertAlign);

                    resizeBounds(x, y);
                    resizeBounds(x+lfWidth, y+lfHeight);
                    firstEffectivePaint = false;
                }
                break;

            case WMFConstants.META_DRAWTEXT:
            case WMFConstants.META_TEXTOUT: {
                    int len = readShort( is );
                    int read = 1; // used to track the actual size really read
                    byte[] bstr = new byte[ len ];
                    for ( int i = 0; i < len; i++ ) {
                        bstr[ i ] = is.readByte();
                    }
                    String sr = WMFUtilities.decodeString(wf, bstr);

                    /* must do this because WMF strings always have an even number of bytes, even
                     * if there is an odd number of characters
                     */
                    if (len % 2 != 0) is.readByte();
                    read += (len + 1) / 2;

                    int y = readShort( is );
                    int x = (int)(readShort( is ) * scaleXY);
                    read += 2;
                    // if the record was not completely read, finish reading
                    if (read < recSize) for (int j = read; j < recSize; j++) readShort( is );
                    TextLayout layout = new TextLayout( sr, wf.font, fontCtx );
                    int lfWidth = (int)layout.getBounds().getWidth();
                    x = (int)layout.getBounds().getX();
                    int lfHeight =
                        (int)getVerticalAlignmentValue(layout, currentVertAlign);

                    resizeBounds(x, y);
                    resizeBounds(x+lfWidth, y+lfHeight);
                }
                break;

            case WMFConstants.META_CREATEFONTINDIRECT: {
                    int lfHeight = readShort( is );
                    float size = (int)(scaleY * lfHeight);
                    int lfWidth = readShort( is );
                    int escape = readShort( is );
                    int orient = readShort( is );
                    int weight = readShort( is );

                    int italic = is.readByte();
                    int underline = is.readByte();
                    int strikeOut = is.readByte();
                    int charset = is.readByte() & 0x00ff;
                    int lfOutPrecision = is.readByte();
                    int lfClipPrecision = is.readByte();
                    int lfQuality = is.readByte();
                    int lfPitchAndFamily = is.readByte();

                    int style = italic > 0 ? Font.ITALIC : Font.PLAIN;
                    style |= (weight > 400) ? Font.BOLD : Font.PLAIN;

                    // don't need to read the end of the record,
                    // because it will always be completely used
                    int len = (2*(recSize-9));
                    byte[] lfFaceName = new byte[ len ];
                    byte ch;
                    for ( int i = 0; i < len; i++ ) lfFaceName[ i ] = is.readByte();
                    String face = new String( lfFaceName );

                    // FIXED : management of font names
                    int d = 0;
                    while   ((d < face.length()) &&
                    ((Character.isLetterOrDigit(face.charAt(d))) ||
                    (Character.isWhitespace(face.charAt(d))))) d++;
                    if (d > 0) face = face.substring(0,d);
                    else face = "System";

                    if ( size < 0 ) size = -size /* * -1.3 */;
                    int objIndex = 0;

                    Font f = new Font(face, style, (int)size);
                    f = f.deriveFont(size);
                    WMFFont wf = new WMFFont(f, charset, underline,
                        strikeOut, italic, weight, orient, escape);

                    objIndex = addObjectAt( FONT, wf , objIndex );
                }
                break;

            case WMFConstants.META_CREATEREGION: {
                int objIndex = 0;
                for ( int j = 0; j < recSize; j++ ) readShort(is); // read all fields
                objIndex = addObjectAt( PALETTE, INTEGER_0, 0 );
                }
                break;

            case WMFConstants.META_CREATEPALETTE: {
                int objIndex = 0;
                for ( int j = 0; j < recSize; j++ ) readShort(is); // read all fields
                objIndex = addObjectAt( OBJ_REGION, INTEGER_0, 0 );
                }
                break;

                case WMFConstants.META_SELECTOBJECT:
                    gdiIndex = readShort(is);
                    if (( gdiIndex & 0x80000000 ) != 0 ) // Stock Object
                        break;

                    gdiObj = getObject( gdiIndex );
                    if ( !gdiObj.used )
                        break;
                    switch( gdiObj.type ) {
                    case PEN:
                        penObject = gdiIndex;
                        break;
                    case BRUSH:
                        brushObject = gdiIndex;
                        break;
                    case FONT: {
                        this.wf =  ((WMFFont)gdiObj.obj);
                        fontObject = gdiIndex;
                        }
                        break;
                    case NULL_PEN:
                        penObject = -1;
                        break;
                    case NULL_BRUSH:
                        brushObject = -1;
                        break;
                    }
                    break;

                case WMFConstants.META_DELETEOBJECT:
                    gdiIndex = readShort(is);
                    gdiObj = getObject( gdiIndex );
                    if ( gdiIndex == brushObject ) brushObject = -1;
                    else if ( gdiIndex == penObject ) penObject = -1;
                    else if ( gdiIndex == fontObject ) fontObject = -1;
                    gdiObj.clear();
                    break;

            case WMFConstants.META_LINETO: {
                    int y = readShort( is );
                    int x = (int)(readShort( is ) * scaleXY);
                    if (penObject >= 0) {
                        resizeBounds(startX, startY);
                        resizeBounds(x, y);
                        firstEffectivePaint = false;
                    }
                    startX = x;
                    startY = y;
                }
                break;
            case WMFConstants.META_MOVETO: {
                    startY = readShort( is );
                    startX = (int)(readShort( is ) * scaleXY);
                }
                break;

            case WMFConstants.META_POLYPOLYGON: {
                    int count = readShort( is );
                    int[] pts = new int[ count ];
                    int ptCount = 0;
                    for ( int i = 0; i < count; i++ ) {
                        pts[ i ] = readShort( is );
                        ptCount += pts[ i ];
                    }

                    int offset = count+1;
                    for ( int i = 0; i < count; i++ ) {
                        for ( int j = 0; j < pts[ i ]; j++ ) {
                            // FIXED 115 : correction preliminary images dimensions
                            int x = (int)(readShort( is ) * scaleXY);
                            int y = readShort( is );
                            if ((brushObject >= 0) || (penObject >= 0)) resizeBounds(x, y);
                        }
                    }
                    firstEffectivePaint = false;
                }
                break;

            case WMFConstants.META_POLYGON: {
                    int count = readShort( is );
                    float[] _xpts = new float[ count+1 ];
                    float[] _ypts = new float[ count+1 ];
                    for ( int i = 0; i < count; i++ ) {
                        _xpts[i] = readShort( is ) * scaleXY;  
                        _ypts[i] = readShort( is );
                    }
                    _xpts[count] = _xpts[0];
                    _ypts[count] = _ypts[0];
                    Polygon2D pol = new Polygon2D(_xpts, _ypts, count);
                    paint(brushObject, penObject, pol);
                }
                break;

                case WMFConstants.META_POLYLINE:
                    {
                        int count = readShort( is );
                        float[] _xpts = new float[ count ];
                        float[] _ypts = new float[ count ];
                        for ( int i = 0; i < count; i++ ) {
                            _xpts[i] = readShort( is ) * scaleXY;  
                            _ypts[i] = readShort( is );
                        }
                        Polyline2D pol = new Polyline2D(_xpts, _ypts, count);
                        paintWithPen(penObject, pol);
                    }
                    break;

            case WMFConstants.META_ELLIPSE:
            case WMFConstants.META_INTERSECTCLIPRECT:
            case WMFConstants.META_RECTANGLE: {
                    int bot = readShort( is );
                    int right = (int)(readShort( is ) * scaleXY);
                    int top = readShort( is );
                    int left = (int)(readShort( is ) * scaleXY);
                    Rectangle2D.Float rec = new Rectangle2D.Float(left, top, right-left, bot-top);
                    paint(brushObject, penObject, rec);
                }
                break;

            case WMFConstants.META_ROUNDRECT: {
                    readShort( is );
                    readShort( is );
                    int bot = readShort( is );
                    int right = (int)(readShort( is ) * scaleXY);
                    int top = readShort( is );
                    int left = (int)(readShort( is ) * scaleXY);
                    Rectangle2D.Float rec = new Rectangle2D.Float(left, top, right-left, bot-top);
                    paint(brushObject, penObject, rec);
                }
                break;

            case WMFConstants.META_ARC:
            case WMFConstants.META_CHORD:
            case WMFConstants.META_PIE: {
                    readShort( is );
                    readShort( is );
                    readShort( is );
                    readShort( is );
                    int bot = readShort( is );
                    int right = (int)(readShort( is ) * scaleXY);
                    int top = readShort( is );
                    int left = (int)(readShort( is ) * scaleXY);
                    Rectangle2D.Float rec = new Rectangle2D.Float(left, top, right-left, bot-top);
                    paint(brushObject, penObject, rec);
                }
                break;

            case WMFConstants.META_PATBLT : {
                    readInt( is ); // rop
                    int height = readShort( is );
                    int width = (int)(readShort( is ) * scaleXY);
                    int left = (int)(readShort( is ) * scaleXY);
                    int top = readShort( is );
                    if (penObject >= 0) resizeBounds(left, top);
                    if (penObject >= 0) resizeBounds(left+width, top+height);
                }
                break;
            // UPDATED : META_DIBSTRETCHBLT added
            case WMFConstants.META_DIBSTRETCHBLT:
                {
                    is.readInt(); // mode
                    readShort( is ); // heightSrc
                    readShort( is ); // widthSrc
                    readShort( is ); // sy
                    readShort( is ); // sx
                    float heightDst = readShort( is );
                    float widthDst = readShort( is ) * scaleXY;
                    float dy = readShort( is ) * getVpWFactor() * inch / PIXEL_PER_INCH;
                    float dx = readShort( is ) * getVpWFactor() * inch / PIXEL_PER_INCH * scaleXY;
                    widthDst = widthDst * getVpWFactor() * inch / PIXEL_PER_INCH;
                    heightDst = heightDst * getVpHFactor() * inch / PIXEL_PER_INCH;
                    resizeImageBounds((int)dx, (int)dy);
                    resizeImageBounds((int)(dx + widthDst), (int)(dy + heightDst));

                    int len = 2*recSize - 20;
                    for (int i = 0; i < len; i++) is.readByte();
                }
                break;
            case WMFConstants.META_STRETCHDIB: {
                is.readInt(); // mode
                readShort( is ); // usage                   
                readShort( is ); // heightSrc
                readShort( is ); // widthSrc
                readShort( is ); // sy
                readShort( is ); // sx
                float heightDst = readShort( is );
                float widthDst = readShort( is ) * scaleXY;
                float dy = readShort( is ) * getVpHFactor() * inch / PIXEL_PER_INCH;
                float dx = readShort( is ) * getVpHFactor() * inch / PIXEL_PER_INCH * scaleXY;
                widthDst = widthDst * getVpWFactor() * inch / PIXEL_PER_INCH;
                heightDst = heightDst * getVpHFactor() * inch / PIXEL_PER_INCH;
                resizeImageBounds((int)dx, (int)dy);
                resizeImageBounds((int)(dx + widthDst), (int)(dy + heightDst));                

                int len = 2*recSize - 22;
                byte[] bitmap = new byte[len];
                for (int i = 0; i < len; i++) bitmap[i] = is.readByte();
            }
            break;                                                
            case WMFConstants.META_DIBBITBLT: {
                is.readInt(); // mode                                        
                readShort( is ); //sy                   
                readShort( is ); //sx
                readShort( is ); // hdc
                float height = readShort( is ) 
                    * (float)inch / PIXEL_PER_INCH * getVpHFactor();
                float width = readShort( is ) 
                    * (float)inch / PIXEL_PER_INCH * getVpWFactor() * scaleXY;
                float dy = 
                    inch / PIXEL_PER_INCH * getVpHFactor() * readShort( is );
                float dx = 
                    inch / PIXEL_PER_INCH * getVpWFactor() * readShort( is ) * scaleXY;
                resizeImageBounds((int)dx, (int)dy);
                resizeImageBounds((int)(dx + width), (int)(dy + height));                        
                }
                break;                
            default:
                for ( int j = 0; j < recSize; j++ )
                    readShort(is);
                break;

            }
        }
        // sets the width, height, etc of the image if the file does not have an APM (in this case it is retrieved
        // from the viewport)
        if (! isAldus) {
            width = vpW;
            height = vpH;
            right = vpX;
            left = vpX + vpW;
            top = vpY;
            bottom = vpY + vpH;
        }        
        resetBounds();
        return true;
    }

    /** @return the width of the Rectangle bounding the figures enclosed in
     * the Metafile, in pixels
     */
    public int getWidthBoundsPixels() {
        return _bwidth;
    }

    /** @return the height of the Rectangle bounding the figures enclosed in
     * the Metafile, in pixels.
     */
    public int getHeightBoundsPixels() {
        return _bheight;
    }

    /** @return the width of the Rectangle bounding the figures enclosed in
     * the Metafile, in Metafile Units.
     */
    public int getWidthBoundsUnits() {
        return (int)((float)inch * _bwidth / PIXEL_PER_INCH);
    }

    /** @return the height of the Rectangle bounding the figures enclosed in
     * the Metafile in Metafile Units.
     */
    public int getHeightBoundsUnits() {
        return (int)((float)inch * _bheight / PIXEL_PER_INCH);
    }

    /** @return the X offset of the Rectangle bounding the figures enclosed in
     * the Metafile.
     */
    public int getXOffset() {
        return _bleft;
    }

    /** @return the Y offset of the Rectangle bounding the figures enclosed in
     * the Metafile.
     */
    public int getYOffset() {
        return _btop;
    }

    private void resetBounds() {
        // calculate geometry size
        scale =  (float)getWidthPixels() / vpW;
        if (_bright != -1) {
            _bright = (int)(scale * (vpX +_bright));
            _bleft = (int)(scale * (vpX +_bleft));
            _bbottom = (int)(scale * (vpY +_bbottom));
            _btop = (int)(scale * (vpY +_btop));
        }

        // calculate image size
        if (_iright != -1) {
            _iright = (int)((float)_iright * getWidthPixels() / width);
            _ileft = (int)((float)_ileft * getWidthPixels() / width);
            _ibottom = (int)((float)_ibottom * getWidthPixels() / width);
            _itop = (int)((float)_itop  * getWidthPixels() / width);

            // merge image and geometry size
            if ((_bright == -1) || (_iright > _bright)) _bright = _iright;
            if ((_bleft == -1) || (_ileft < _bleft)) _bleft = _ileft;
            if ((_btop == -1) || (_itop < _btop)) _btop = _itop;
            if ((_bbottom == -1) || (_ibottom > _bbottom)) _bbottom = _ibottom;
        }

        if ((_bleft != -1) && (_bright != -1)) _bwidth = _bright - _bleft;
        if ((_btop != -1) && (_bbottom != -1)) _bheight = _bbottom - _btop;
    }

    /** resize Bounds for each primitive encountered. Only elements that are in the overall
     * width and height of the Metafile are kept.
     */
    private void resizeBounds(int x, int y) {
        if (_bleft == -1) _bleft = x;
        else if (x < _bleft) _bleft = x;
        if (_bright == -1) _bright = x;
        else if (x > _bright) _bright = x;

        if (_btop == -1) _btop = y;
        else if (y < _btop) _btop = y;
        if (_bbottom == -1) _bbottom = y;
        else if (y > _bbottom) _bbottom = y;
    }

    /** resize Bounds for each image primitive encountered. Only elements that are in the overall
     * width and height of the Metafile are kept.
     */
    private void resizeImageBounds(int x, int y) {
        if (_ileft == -1) _ileft = x;
        else if (x < _ileft) _ileft = x;
        if (_iright == -1) _iright = x;
        else if (x > _iright) _iright = x;

        if (_itop == -1) _itop = y;
        else if (y < _itop) _itop = y;
        if (_ibottom == -1) _ibottom = y;
        else if (y > _ibottom) _ibottom = y;
    }

    /** get the Color corresponding with the Object (pen or brush object).
     */
    private Color getColorFromObject(int brushObject) {
        Color color = null;
        if ( brushObject >= 0 ) {
            GdiObject gdiObj = getObject( brushObject );
            return  (Color)gdiObj.obj;
        } else return null;
    }

    /** Resize the bounds of the WMF image according with the bounds of the geometric
     *  Shape.
     *  There will be no resizing if one of the following properties is true :
     *  <ul>
     *  <li>the brush and the pen objects are < 0 (null objects)</li>
     *  <li>the color of the geometric Shape is white, and no other Shapes has occured</li>
     *  </ul>
     */
    private void paint(int brushObject, int penObject, Shape shape) {
        if (( brushObject >= 0 ) || (penObject >= 0)) {
            Color col;
            if (brushObject >= 0) col = getColorFromObject(brushObject);
            else col = getColorFromObject(penObject);

            if (!(firstEffectivePaint && (col.equals(Color.white)))) {
                Rectangle rec = shape.getBounds();
                resizeBounds((int)rec.getMinX(), (int)rec.getMinY());
                resizeBounds((int)rec.getMaxX(), (int)rec.getMaxY());
                firstEffectivePaint = false;
            }
        }
    }

    /** Resize the bounds of the WMF image according with the bounds of the geometric
     *  Shape.
     *  There will be no resizing if one of the following properties is true :
     *  <ul>
     *  <li>the pen objects is < 0 (null object)</li>
     *  <li>the color of the geometric Shape is white, and no other Shapes has occured</li>
     *  </ul>
     */
    private void paintWithPen(int penObject, Shape shape) {
        if (penObject >= 0) {
            Color col = getColorFromObject(penObject);

            if (!(firstEffectivePaint && (col.equals(Color.white)))) {
                Rectangle rec = shape.getBounds();
                resizeBounds((int)rec.getMinX(), (int)rec.getMinY());
                resizeBounds((int)rec.getMaxX(), (int)rec.getMaxY());
                firstEffectivePaint = false;
            }
        }
    }

    /** get the vertical Alignment value for the text.
     */
    private float getVerticalAlignmentValue(TextLayout layout, int vertAlign) {
        if (vertAlign == WMFConstants.TA_BASELINE) return -layout.getAscent();
        else if (vertAlign == WMFConstants.TA_TOP) return (layout.getAscent() + layout.getDescent());
        else return 0;
    }
}
