All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.cq.dam.handler.standard.msoffice.wmf.WmfDecoder Maven / Gradle / Ivy

/*
 * Copyright 1997-2011 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
package com.day.cq.dam.handler.standard.msoffice.wmf;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.Dimension;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.Color;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.FontMetrics;
import java.awt.image.ColorModel;
import java.awt.image.ImageConsumer;
import java.awt.image.ImageProducer;
import java.awt.image.PixelGrabber;
import java.awt.image.ImageObserver;
import java.awt.image.MemoryImageSource;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.EOFException;
import java.util.Stack;
import java.util.Vector;
import java.util.Enumeration;

/**
 * WMF file interpreter.
 * Taken from old DAM media handler project com.day.cq.dam.media.handler.office.wmf
 */
public class WmfDecoder implements ImageProducer {

    private static final Logger log = LoggerFactory.getLogger(WmfDecoder.class);

    boolean drawCross_if_error = true;
    private int minsize = 7;
    private int top, left, siz, obj, max;
    private int res;
    private int inch;
    private WmfDecObj gdiObj[];
    private Stack DCstack;
    private int rgbPixels[] = null;
    private short params[];
    private int width = -1, height = -1;
    private InputStream in;
    private ColorModel cmodel = ColorModel.getRGBdefault();
    private boolean err = false;
    private boolean producing = false;
    private Vector consumers = new Vector();
    private byte[] data;
    private int factor = 1;

    // constructor
    public WmfDecoder(byte[] data) {
        this.data = data;
    }

    private InputStream getStream() {
        return new ByteArrayInputStream(this.data);
    }

    // -------- methods that implement ImageProducer -----------------------
    public void addConsumer(ImageConsumer ic) {
        if (log.isDebugEnabled()) {
            log.debug("addConsumer:" + ic);
        }
        if (ic != null && !this.isConsumer(ic)) {
            this.consumers.addElement(ic);
        }
    }

    public void startProduction(ImageConsumer ic) {
        if (log.isDebugEnabled()) {
            log.debug("startProduction:" + ic);
        }
        this.addConsumer(ic);
        if (this.rgbPixels == null) {
            try {
                this.readWmf();
            } catch (Exception ex) {
                this.err = true;
                this.width = this.height = -1;
                log.error("Error while reading wmf", ex);
            }
        }

        if (!this.producing) {
            this.producing = true;
            this.sendImage();
        }
    }

    public boolean isConsumer(ImageConsumer ic) {
        return this.consumers.contains(ic);
    }

    public void removeConsumer(ImageConsumer ic) {
        if (log.isDebugEnabled()) {
            log.debug("Remove:" + ic);
        }
        this.consumers.removeElement(ic);
    }

    public void requestTopDownLeftRightResend(ImageConsumer ic) {
    }

    // ---- method connects the Wmf interpreter with ImageProducer methods
    private void sendImage() {
        Vector xconsumers = (Vector) this.consumers.clone();
        // consumers will decrease while calling ic.imageComplete()
        // so in xconsumers we can each all elements
        for (Enumeration e = xconsumers.elements(); e.hasMoreElements();) {
            if (log.isDebugEnabled()) {
                log.debug("consumers.size:" + this.consumers.size());
            }
            ImageConsumer ic = (ImageConsumer) e.nextElement();
            if (this.isConsumer(ic)) {
                if (log.isDebugEnabled()) {
                    log.debug("setPixels:" + ic);
                }
                if (!this.err) {
                    ic.setDimensions(this.width, this.height);
                    ic.setColorModel(this.cmodel);
                    ic.setHints(ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES
                            | ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME);
                    for (int row = 0; row < this.height; row++)
                        ic.setPixels(0, row, this.width, 1, this.cmodel, this.rgbPixels, row
                                * this.width, this.width);
                    ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
                } else {
                    if (log.isDebugEnabled()) {
                        log.debug("IMAGEERROR");
                    }
                    ic.imageComplete(ImageConsumer.IMAGEABORTED);
                }
            }
        }
        this.producing = false;
    }

    private static void printObserverStatus(String text, int status) {
        log.info(text);
        if ((status & 0x80) > 0)
            log.info("ABORT ");
        if ((status & 0x40) > 0)
            log.info("ERROR ");
        if ((status & 0x20) > 0)
            log.info("ALLBITS ");
        if ((status & 0x10) > 0)
            log.info("FRAMEBITS ");
        if ((status & 0x08) > 0)
            log.info("SOMEBITS ");
        if ((status & 0x04) > 0)
            log.info("PROPERTIES ");
        if ((status & 0x02) > 0)
            log.info("HEIGHT ");
        if ((status & 0x01) > 0)
            log.info("WIDTH ");
        log.info("");
    }

    public Dimension getDimension() throws IOException {
        int i, rdSize, rdFunc;
        this.in = this.getStream();
        this.chkHeader(this.in, null);
        this.params = new short[this.max]; // max space for a metafile record
        boolean go = true;
        while (go) {
            try {
                rdSize = this.readInt32(this.in);
                rdFunc = this.readInt16(this.in);
                for (i = 0; i < rdSize - 3; i++) {
                    this.params[i] = this.readInt16(this.in);
                }

                switch (rdFunc) {
                    case META_SETWINDOWEXT:
                        if (this.params[0] > this.params[1] && this.params[0] > 2000) {
                            this.factor = 4;
                        } else if (this.params[1] > this.params[0] && this.params[1] > 2000) {
                            this.factor = 4;
                        } else {
                            this.factor = 1;
                        }
                        go = false;
                        return new Dimension(this.params[1] / this.factor, this.params[0] / this.factor);
                }
            } catch (IOException ex) {
                go = false;
            }
        }
        // return default values
        return new Dimension(320, 240);
    }

    // ---- main method for reading Wmf into a pixel array -------------------
    private void readWmf() throws IOException, InterruptedException {
        Dimension d = this.getDimension();
        this.in = this.getStream();
        Image offscreen;
        Graphics g;

        if (this.chkHeader(this.in, d)) {
            throw new IOException("WMF file format not supported");
        }

        this.DCstack = new Stack();
        this.gdiObj = new WmfDecObj[this.obj];
        this.width = d.width;
        this.height = d.height;

        log.debug("Dimension:" + d);
        offscreen = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_RGB);
        g = offscreen.getGraphics();
        // new todo
        g.setColor(Color.white);
        g.fillRect(0, 0, d.width, d.height);
        //
        this.params = new short[this.max]; // max space for a metafile record

        WmfDecDC DC = new WmfDecDC(this.width, this.height, this.left, this.top);
        DC.gr = g;
        this.DCstack.push(DC);
        while (this.readRecord(this.in))
            ;
        this.rgbPixels = new int[d.width * d.height];
        PixelGrabber pg = new PixelGrabber(offscreen.getSource(), 0, 0, d.width, d.height,
                this.rgbPixels, 0, d.width);
        pg.grabPixels();
        if (log.isDebugEnabled()) {
            printObserverStatus("PixelGrabber status: ", pg.status());
        }
        log.debug("PixelGrabber status:" + pg.status());
        g.dispose();
    }

    private boolean chkHeader(InputStream in, Dimension d) throws IOException {
        int i, j, wid = 0, hig = 0, sum = 0;
        int hdr[] = { -12841, -25914, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 9, 0x300 };

        for (i = 0; i < 14; i++) {
            j = this.readInt16(in);
            sum ^= j;
            if ((i < 3 || i > 7)) {
                if (j != hdr[i]) {
                    if ((i += 11) == 11 && j == hdr[11]) { // no first header
                        // present
                        continue;
                    } else {
                        return true; // error
                    }

                }
            } else {
                switch (i) {
                    case 3:
                        this.left = j;
                        break;
                    case 4:
                        this.top = j;
                        break;
                    case 5:
                        wid = j;
                        break;
                    case 6:
                        hig = j;
                        break;
                    case 7:
                        hdr[10] = sum; // store checksum
                        this.res = Toolkit.getDefaultToolkit().getScreenResolution();
                        this.inch = j;
                        if (log.isDebugEnabled()) {
                            log.debug("inch:  " + this.inch);
                            log.debug("sres:  " + this.res);
                        }
                        d.width = ((wid - this.left) * this.res) / this.inch;
                        d.height = ((hig - this.top) * this.res) / this.inch;
                        break;
                }
            }
        }
        log.debug("dimension: " + d);
        this.siz = this.readInt32(in);
        this.obj = this.readInt16(in);
        this.max = this.readInt32(in);
        this.readInt16(in); // unused

        log.debug("filesize(16): " + this.siz);
        log.debug("GDI-Objects : " + this.obj);
        log.debug("max rec size: " + this.max);

        return false;
    }

    private boolean readRecord(InputStream in) {
        int i, j, rdSize, rdFunc;
        int a, b, c, d, e, f, k, l, m, n;
        Color crco;
        Font fo;
        Image im;
        WmfDecDC DC = (WmfDecDC) this.DCstack.peek();
        Graphics g = DC.gr;

        boolean error;
        int xpoints[], ypoints[];
        byte text[];
        String s;
        Object ob;
        Graphics g2;

        try {
            rdSize = this.readInt32(in);
            rdFunc = this.readInt16(in);
            for (i = 0; i < rdSize - 3; i++)
                this.params[i] = this.readInt16(in);
        } catch (IOException ex) {
            return false;
        }
        if (log.isDebugEnabled()) {
            log.debug("RFunc: " + Integer.toString(rdFunc, 16));
        }
        switch (rdFunc) {
            case META_LINETO:
                if (log.isDebugEnabled()) {
                    log.debug("MetaLineTo");
                }
                g.setColor(DC.aktpen.getColor());

                a = DC.ytransfer(this.params[0]);
                b = DC.xtransfer(this.params[1]);
                g.drawLine(DC.aktXpos, DC.aktYpos, b, a);
                DC.aktXpos = b;
                DC.aktYpos = a;
                break;

            case META_MOVETO:
                if (log.isDebugEnabled()) {
                    log.debug("MetaMoveTo");
                }
                DC.aktYpos = DC.ytransfer(this.params[0]);
                DC.aktXpos = DC.xtransfer(this.params[1]);
                break;

            case META_ROUNDRECT:
                if (log.isDebugEnabled()) {
                    log.debug("MetaRoundRect");
                }
                e = this.transform(this.params[0], this.minsize);
                f = this.transform(this.params[1], this.minsize);
                a = DC.ytransfer(this.params[2]);
                b = DC.xtransfer(this.params[3]);
                c = DC.ytransfer(this.params[4]);
                d = DC.xtransfer(this.params[5]);
                if (a < c && b < d) {
                    i = a;
                    a = c;
                    c = i;
                    i = b;
                    b = d;
                    d = i;
                }

                // FIXME: draw_round_rect_Pattern if needed
                g.setColor(DC.aktbrush.getColor());
                g.fillRoundRect(d, c, b - d - 1, a - c - 1, f, e);
                g.setColor(DC.aktpen.getColor());
                g.drawRoundRect(d, c, b - d - 1, a - c - 1, f, e);
                break;

            case META_RECTANGLE:
                if (log.isDebugEnabled()) {
                    log.debug("MetaRectangle");
                }
                a = DC.ytransfer(this.params[0]);
                b = DC.xtransfer(this.params[1]);
                c = DC.ytransfer(this.params[2]);
                d = DC.xtransfer(this.params[3]);
                if (a < c && b < d) {
                    i = a;
                    a = c;
                    c = i;
                    i = b;
                    b = d;
                    d = i;
                }
                if (DC.aktbrush.getImage() != null) {
                    this.drawOpaqePattern(g, DC.aktbrush.getImage(), d, c, b, a, null);// fr);
                    // //todo
                    log.debug("*** drawOpaqePattern");
                } else {
                    g.setColor(DC.aktbrush.getColor());
                    g.fillRect(d, c, b - d - 1, a - c - 1);
                }
                g.setColor(DC.aktpen.getColor());
                g.drawRect(d, c, b - d - 1, a - c - 1);
                break;

            case META_SETPIXEL:
                if (log.isDebugEnabled()) {
                    log.debug("MetaSetpixel");
                }
                crco = new Color(this.getLoByteVal(this.params[0]), this.getHiByteVal(this.params[0]),
                        this.getLoByteVal(this.params[1]));
                g.setColor(crco);
                crco = null;
                a = DC.xtransfer(this.params[3]);
                b = DC.ytransfer(this.params[2]);
                g.drawLine(a, b, a, b);
                break;

            case META_POLYLINE:
            case META_POLYGON:
                if (log.isDebugEnabled()) {
                    log.debug(((rdFunc == META_POLYGON) ? "MetaPolygon: " : "MetaPolyLine: ")
                            + this.params[0]);
                }
                xpoints = new int[this.params[0]];
                ypoints = new int[this.params[0]];
                for (i = 0; i < this.params[0]; i++) {
                    xpoints[i] = DC.xtransfer(this.params[i * 2 + 1]);
                    ypoints[i] = DC.ytransfer(this.params[i * 2 + 2]);
                    if (log.isDebugEnabled()) {
                        log.debug(Integer.toString(xpoints[i], 16) + " "
                                + Integer.toString(ypoints[i], 16));
                    }
                }
                if (rdFunc == META_POLYGON) {
                    g.setColor(DC.aktbrush.getColor());
                    g.fillPolygon(xpoints, ypoints, this.params[0]);
                    g.setColor(DC.aktpen.getColor());
                    g.drawPolygon(xpoints, ypoints, this.params[0]);
                } else {
                    g.setColor(DC.aktpen.getColor());
                    g.drawPolyline(xpoints, ypoints, this.params[0]);
                }
                xpoints = null;
                ypoints = null;
                break;

            case META_POLYPOLYGON:
                if (log.isDebugEnabled()) {
                    log.debug("MetaPolyPolygon: " + this.params[0]);
                }

                for (i = 0; i < this.params[0]; i++) {
                    xpoints = new int[this.params[i + 1]];
                    ypoints = new int[this.params[i + 1]];
                    if (log.isDebugEnabled()) {
                        log.debug("Polygon #" + i + " Pts=" + this.params[i + 1]);
                    }

                    b = this.params[0] + 1; // first point of first polygon
                    for (c = 0; c < i; c++)
                        b += this.params[c + 1] * 2; // add size of polygons
                    // before

                    for (a = 0; a < this.params[i + 1]; a++) {
                        xpoints[a] = DC.xtransfer(this.params[b + a * 2]);
                        ypoints[a] = DC.ytransfer(this.params[b + a * 2 + 1]);
                        if (log.isDebugEnabled()) {
                            log.debug(Integer.toString(xpoints[a], 16) + " "
                                    + Integer.toString(ypoints[a], 16));
                        }
                    }
                    g.setColor(DC.aktbrush.getColor());
                    g.fillPolygon(xpoints, ypoints, this.params[i + 1]);
                    g.drawPolygon(xpoints, ypoints, this.params[i + 1]);
                }
                break;

            case META_ELLIPSE:
                if (log.isDebugEnabled()) {
                    log.debug("MetaEllipse");
                }
                a = DC.ytransfer(this.params[0]);
                b = DC.xtransfer(this.params[1]);
                c = DC.ytransfer(this.params[2]);
                d = DC.xtransfer(this.params[3]);
                g.setColor(DC.aktpen.getColor());
                g.drawOval(d, c, b - d, a - c);
                g.setColor(DC.aktbrush.getColor());
                g.fillOval(d, c, b - d, a - c);
                break;

            case META_ARC:
            case META_PIE:
            case META_CHORD:
                if (log.isDebugEnabled())
                    switch (rdFunc) {
                        case META_ARC:
                            log.debug("MetaArc");
                            break;
                        case META_PIE:
                            log.debug("MetaPie");
                            break;
                        case META_CHORD:
                            log.debug("MetaChord");
                            break;
                    }

                a = DC.ytransfer(this.params[0]);
                b = DC.xtransfer(this.params[1]); // Yend;Xend;
                c = DC.ytransfer(this.params[2]);
                d = DC.xtransfer(this.params[3]); // Ystart;Xstart;
                e = DC.ytransfer(this.params[4]);
                f = DC.xtransfer(this.params[5]); // Ybuttom;Xright;
                k = DC.ytransfer(this.params[6]);
                l = DC.xtransfer(this.params[7]); // Ytop;Xleft

                g.setColor(DC.aktpen.getColor());
                int xm = l + (f - l) / 2;
                int ym = k + (e - k) / 2;
                if (rdFunc == META_PIE) {
                    g.drawLine(d, c, xm, ym);
                    g.drawLine(b, a, xm, ym);
                }
                if (rdFunc == META_CHORD)
                    g.drawLine(d, c, b, a);

                int beg = this.arcus(d - xm, c - ym);
                int arc = this.arcus(b - xm, a - ym) - beg;
                if (arc < 0)
                    arc += 360;
                if (log.isDebugEnabled()) {
                    log.debug("Beg=" + beg + " Arc=" + arc);
                }
                g.drawArc(l, k, f - l, e - k, beg, arc);
                //
                // FIXME: fill arc etc with selected brush Sat May 17 19:02:27 1997
                break;

            case META_DELETEOBJECT:
                if (log.isDebugEnabled()) {
                    log.debug("MetaDeleteObject:" + this.params[0]);
                }
                this.gdiObj[this.params[0]] = null;
                break;

            case META_SELECTPALETTE:
                if (log.isDebugEnabled()) {
                    log.debug("MetaSelectPalette:" + this.params[0] + " = "
                            + this.gdiObj[this.params[0]]);
                }
                if (this.gdiObj[this.params[0]].getMagic() == WmfDecObj.M_PALETTE)
                    DC.aktpal = this.gdiObj[this.params[0]];
                else
                    log.error(" ---- internal ERROR in MetaSelectPalette -----");
                break;

            case META_SELECTCLIPREGION:
                if (log.isDebugEnabled()) {
                    log.debug("MetaSelectClipRegion:" + this.params[0] + " = "
                            + this.gdiObj[this.params[0]]);
                }
                if (this.gdiObj[this.params[0]] != null
                        && this.gdiObj[this.params[0]].getMagic() == WmfDecObj.M_CLIP) {
                    DC.aktclip = this.gdiObj[this.params[0]];
                    g.clipRect(DC.aktclip.getRect().x, DC.aktclip.getRect().y,
                            DC.aktclip.getRect().width, DC.aktclip.getRect().height);
                } else
                    log.error(" ---- internal ERROR in MetaSelectClipregion -----");
                break;

            case META_SELECTOBJECT:
                if (log.isDebugEnabled()) {
                    log.debug("MetaSelectObject:" + this.params[0] + " = "
                            + this.gdiObj[this.params[0]]);
                }
                switch (this.gdiObj[this.params[0]].getMagic()) {
                    case WmfDecObj.M_PEN:
                        DC.aktpen = this.gdiObj[this.params[0]];
                        break;
                    case WmfDecObj.M_FONT:
                        DC.aktfont = this.gdiObj[this.params[0]];
                        break;
                    case WmfDecObj.M_BRUSH:
                        DC.aktbrush = this.gdiObj[this.params[0]];
                        break;
                    case WmfDecObj.M_PALETTE: // a kind of dummy
                        DC.aktpal = this.gdiObj[this.params[0]];
                        break;
                    case WmfDecObj.M_BITMAP: // another one...
                        DC.aktbmp = this.gdiObj[this.params[0]];
                        break;
                    case WmfDecObj.M_CLIP:
                        DC.aktclip = this.gdiObj[this.params[0]];
                        if (log.isDebugEnabled()) {
                            log.debug("Select clipping rect");
                            //g.drawRect(DC.aktclip.getRect().x, DC.aktclip.getRect().y,
                            //        DC.aktclip.getRect().width, DC.aktclip.getRect().height);
                        }
                        g.clipRect(DC.aktclip.getRect().x, DC.aktclip.getRect().y,
                                DC.aktclip.getRect().width, DC.aktclip.getRect().height);
                        break;
                }
                break;

            case META_CREATEPENINDIRECT:
                if (log.isDebugEnabled()) {
                    log.debug("MetaCreatePenIndirect");
                }
                error = false;
                switch (this.params[0]) {
                    case PS_NULL:
                        crco = null; // ex DC.aktbackgnd;
                        // FIXME: have to test all DC.akt{pen|brush}.getColor()
                        // if color equals null, do NOT paint,draw,fill etc!! Sat May 17
                        // 19:23:45 1997
                        log.debug("MetaCreatePenIndirect: PS_NULL");
                        break;
                    case PS_DASH:
                    case PS_DOT:
                    case PS_DASHDOT:
                    case PS_DASHDOTDOT:
                        log.debug("MetaCreatePenIndirect: line attribute " + this.params[0]
                                + " ignored");
                    case PS_INSIDEFRAME:
                    case PS_SOLID:
                        crco = new Color(this.getLoByteVal(this.params[3]), this
                                .getHiByteVal(this.params[3]), this.getLoByteVal(this.params[4]));
                        break;
                    default:
                        crco = Color.black;
                        error = true;
                        break;
                }
                if (!error) {
                    this.add_handle(new WmfDecObj(PS_SOLID /* params[0] */, crco));
                    if (log.isDebugEnabled()) {
                        log.debug(crco.toString());
                    }
                    crco = null;
                    a = this.params[1];
                    b = this.params[2];
                }
                if (log.isDebugEnabled() || error) {
                    for (i = 0; i < rdSize - 3; i++)
                        if (i < 16)
                            log.debug(Integer.toString(this.params[i], 16) + " ");
                    //System.out.println();
                }
                break;

            case META_CREATEBRUSHINDIRECT:
                if (log.isDebugEnabled()) {
                    log.debug("MetaCreateBrushIndirect: Style=" + this.params[0]);
                    this.showparams(this.params, rdSize, rdFunc);
                }

                switch (this.params[0]) {
                    case 1:
                        crco = DC.aktbackgnd; // BS_HOLLOW
                        this.add_handle(new WmfDecObj(crco, WmfDecObj.M_BRUSH));
                        if (log.isDebugEnabled()) {
                            log.debug(crco.toString());
                        }
                        break;
                    case 0:
                        crco = new Color(this.getLoByteVal(this.params[1]), // BS_SOLID
                                this.getHiByteVal(this.params[1]), this.getLoByteVal(this.params[2]));
                        this.add_handle(new WmfDecObj(crco, WmfDecObj.M_BRUSH));
                        if (log.isDebugEnabled()) {
                            log.debug(crco.toString());
                        }
                        crco = null;
                        break;
                    case 2:
                        crco = new Color(this.getLoByteVal(this.params[1]), // BS_HATCHED
                                this.getHiByteVal(this.params[1]), this.getLoByteVal(this.params[2]));
                        this.add_handle(new WmfDecObj(this.params[3], crco, DC.aktbackgnd));// ,fr));
                        if (log.isDebugEnabled()) {
                            log.debug(crco.toString());
                        }
                        crco = null;
                        break;

                    case 3: // BS_PATTERN
                    case 4: // BS_INDEXED
                    case 5: // BS_DIBPATTERN
                        // FIXME: replace workaround
                        crco = Color.gray;
                        this.add_handle(new WmfDecObj(crco, WmfDecObj.M_BRUSH));
                        log.debug("pattern substitution used.");
                        break;

                    default:
                        log.warn("(bad parameter!)");
                }
                break;

            case META_CREATEREGION:
                if (log.isDebugEnabled()) {
                    log.debug("MetaCreateRegion");
                    log.debug("params[5] sub records=" + this.params[5]);
                    for (i = 0; i < rdSize - 3; i++)
                        log.debug(Integer.toString(this.params[i], 10) + " ");
                    //System.out.println();
                }
                this.add_handle(new WmfDecObj(DC.xtransfer(this.params[7]), DC
                        .ytransfer(this.params[8]), DC.xtransfer(this.params[9]), DC
                        .xtransfer(this.params[10])));
                // awt supports only rectangle clipping, currently other data
                // ignored
                // FIXME: fake oval or other regions
                break;

            case META_INTERSECTCLIPRECT:
                // if (debug)
                log.debug("MetaIntersectClipRect is experimental");
                n = DC.ytransfer(this.params[0]);
                m = DC.xtransfer(this.params[1]);
                l = DC.ytransfer(this.params[2]);
                k = DC.xtransfer(this.params[3]);
                g.clipRect(k, l, m - k, n - l);
                break;

            case META_CREATEFONTINDIRECT:
                text = new byte[80];
                for (j = i = 0; i < rdSize - 3 - 9; i++) // 9 starts FontName, 3
                        // for overhead
                {
                    if ((text[2 * i] = (byte) this.getLoByteVal(this.params[i + 9])) == 0)
                        break;
                    else
                        j++;
                    if ((text[2 * i + 1] = (byte) this.getHiByteVal(this.params[i + 9])) == 0)
                        break;
                    else
                        j++;
                }
                s = new String(text, 0, 0, j);
                if (log.isDebugEnabled()) {
                    log.debug("MetaCreateFontIndirect: " + this.params[0] + " "
                            + this.params[1] + " " + s);
                }
                if (s.startsWith("Times")) // may be: "Times New Roman"
                    s = "TimesRoman";
                else if (s.startsWith("Arial"))
                    s = "Helvetica";
                else if (s.startsWith("Courier"))
                    s = "Courier";
                else if (s.startsWith("MS")) // may be: "MS Sans Serif"
                    s = "Dialog";
                else if (s.startsWith("WingDings"))
                    s = "ZapfDingbats";
                b = this.params[1]; // width
                c = this.params[2];
                d = this.params[3]; // esc, ori
                e = this.params[4];
                f = this.params[5]; // weight, ita+underl
                k = this.params[6];
                l = this.params[7]; // str+cha, out+clip
                i = this.params[8]; // pitch
                a = this.transform(this.params[0], this.minsize);
                fo = new Font(s, (e > 500 ? Font.BOLD : Font.PLAIN)
                        + (this.getLoByteVal(f) > 0 ? Font.ITALIC : 0), a);
                if (log.isDebugEnabled()) {
                    log.debug(fo.toString());
                }
                this.add_handle(new WmfDecObj(fo, this.getHiByteVal(f), d));
                fo = null;
                text = null;
                break;

            case META_CREATEPALETTE:
                if (log.isDebugEnabled()) {
                    log.debug("MetaCreatePalette");
                }
                crco = Color.black;
                this.add_handle(new WmfDecObj(crco, WmfDecObj.M_PALETTE));
                break;

            case META_REALIZEPALETTE:
                if (log.isDebugEnabled()){
                    this.showparams(this.params, rdSize, rdFunc);
                }
                log.debug("MetaRealizePalette");
                break;

            case META_SETROP2:
                if (log.isDebugEnabled()) {
                    log.debug("MetaSetRop2: ROP code="
                            + Integer.toString((i = this.params[0]), 16));
                }
                break;

            case META_SETPOLYFILLMODE:
                if (log.isDebugEnabled()) {
                    log.debug("MetaSetPolyFillmode:" + this.params[0]);
                }
                break;

            case META_SETSTRETCHBLTMODE:
                if (log.isDebugEnabled()) {
                    log.debug("MetaSetStretchBltMode:" + this.params[0]);
                }
                break;

            case META_INVERTREGION:
                if (log.isDebugEnabled()) {
                    this.showparams(this.params, rdSize, rdFunc);
                }
                log.debug("MetaInvertRegion:" + this.params[0]);
                break;

            case META_SETWINDOWEXT:
                DC.winextY = this.params[0];
                DC.winextX = this.params[1];
                if (log.isDebugEnabled()) {
                    log.debug("MetaSetWindowExt:  X:" + DC.winextX + "  Y:" + DC.winextY);
                }
                break;

            case META_SETWINDOWORG:
                DC.winorgY = this.params[0];
                DC.winorgX = this.params[1];
                if (log.isDebugEnabled()) {
                    log.debug("MetaSetWindowOrg:  X:" + DC.winorgX + "  Y:" + DC.winorgY);
                }
                break;

            case META_SETTEXTCOLOR:
                DC.akttextc = new Color(this.getLoByteVal(this.params[0]), this
                        .getHiByteVal(this.params[0]), this.getLoByteVal(this.params[1]));
                if (log.isDebugEnabled()) {
                    log.debug("MetaSetTextColor: " + DC.akttextc);
                }
                break;

            case META_EXTTEXTOUT:
            case META_TEXTOUT:
                if (rdFunc == META_EXTTEXTOUT) {
                    a = this.params[2]; // text length
                    b = DC.ytransfer(this.params[0]);
                    c = DC.xtransfer(this.params[1]);
                    d = this.params[3]; // option
                    if (log.isDebugEnabled()) {
                        log.debug("ExtTextOut:option =" + Integer.toString(d, 16));
                    }

                    k = DC.xtransfer(this.params[4]);
                    l = DC.ytransfer(this.params[5]);
                    m = DC.xtransfer(this.params[6]);
                    n = DC.ytransfer(this.params[7]);

                    log.debug("TextAlign=" + DC.akttextalign);
                    log.debug("x  =" + c + "\ty  =" + b);
                    log.debug("rx =" + k + "\try =" + l);
                    log.debug("rw =" + (m - k) + "\trh =" + (n - l));

                    e = d == 0 ? 3 : 7; // start of text
                } else {
                    a = this.params[0]; // text length
                    b = DC.ytransfer(this.params[(a + 1) / 2 + 1]);
                    c = DC.xtransfer(this.params[(a + 1) / 2 + 2]);
                    d = e = 0;
                    k = l = m = n = 0;
                }
                // ------- handle ETO_... flags
                if ((d & ETO_OPAQUE) != 0) {
                    g.setColor(DC.aktbackgnd); // for testing purpose: ....
                    // ,Color.green);
                    g.fillRect(k, l, m - k - 1, n - l - 1);
                    if (log.isDebugEnabled()) {
                        log.debug("ExtTextOut: using OPAQUE style");
                    }
                }
                //
                if ((d & ETO_GRAYED) != 0) {
                    g.setColor(Color.lightGray);
                } else {
                    g.setColor(DC.akttextc);
                }
                //
                if ((d & ETO_CLIPPED) != 0) {
                    g2 = g.create();
                    g2.clipRect(k, l, m - k - 1, n - l - 1);
                    // FIXME: intersect with original clip rect
                    g = g2;
                    log.debug("ExtTextOut: using clipping rect");
                } else {
                    g2 = null;
                }
                // ------------------
                g.setFont(DC.aktfont.getFont());
                FontMetrics fm = g.getFontMetrics();
                text = new byte[a];
                for (i = 0; i < a; i++) {
                    if (i % 2 == 0) {
                        text[i] = (byte) this.getLoByteVal(this.params[e + i / 2 + 1]);
                    } else {
                        text[i] = (byte) this.getHiByteVal(this.params[e + i / 2 + 1]);
                    }
                }
                s = new String(text);
                // ---- draw text ---
                if (DC.aktfont.getFontOrientation() != 0) {
                    g.drawString(s, c, b);

                    log.warn("non horizontal text is not supported: " + s);
                } else {
                    if (DC.akttextalign == TA_TOP) {
                        b += DC.aktfont.getFont().getSize();
                    }
                    g.drawString(s, c, b);
                    if (DC.aktfont.isUnderlined()) {
                        g.drawLine(c, b + 2, c + fm.stringWidth(s), b + 2);
                    }
                }
                // ------------------
                if (log.isDebugEnabled()) {
                    log.debug((rdFunc == META_EXTTEXTOUT ? "MetaExtTextOut: "
                            : "MetaTextOut: ")
                            + (new String(text, 0)) + " (len=" + a + ") x=" + c + " y=" + b);
                }
                text = null;
                if (g2 != null)
                    g2.dispose();
                break;

            case META_SETMAPMODE:
                if (log.isDebugEnabled()) {
                    this.showparams(this.params, rdSize, rdFunc);
                }
                log.debug("MetaSetMapMode: " + this.params[0] + " (ignored)");
                break;

            case META_SETBKCOLOR:
                if (log.isDebugEnabled()) {
                    log.debug("MetaSetBkColor");
                }
                DC.aktbackgnd = new Color(this.getLoByteVal(this.params[0]), this
                        .getHiByteVal(this.params[0]), this.getLoByteVal(this.params[1]));
                break;

            case META_SETTEXTJUSTIFICATION:
                if (log.isDebugEnabled()) {
                    this.showparams(this.params, rdSize, rdFunc);
                }
                if (log.isDebugEnabled() || this.params[0] != 0 || this.params[1] != 0)
                    log.debug("MetaSetTextJustification: " + this.params[0] + " "
                            + this.params[1]);
                break;

            case META_SETBKMODE:
                if (log.isDebugEnabled()) {
                    log.debug("MetaSetBkMode:"
                            + (this.params[0] == 1 ? "TRANSPARENT" : "OPAQUE"));
                }
                DC.aktbkmode = this.params[0];
                break;

            case META_SETTEXTALIGN:
                if (log.isDebugEnabled()) {
                    log.debug("MetaSetTextalign: " + this.params[0]);
                }
                DC.akttextalign = this.params[0];
                break;

            case META_SAVEDC:
                if (log.isDebugEnabled()) {
                    log.debug("MetaSaveDC");
                }
                try {
                    DC = (WmfDecDC) this.DCstack.push(DC.clone());
                    DC.slevel++;
                    DC.gr = g.create();
                } catch (Exception ex) {
                    log.error(" ---- internal ERROR in MetaSaveDC -----");
                }
                break;

            case META_RESTOREDC:
                if (log.isDebugEnabled()) {
                    log.debug("MetaRestoreDC" + this.params[0]);
                }
                switch (this.params[0]) {
                    case -1:
                        g.dispose();
                        this.DCstack.pop();
                        DC = (WmfDecDC) this.DCstack.peek();
                        break;
                    default:
                        while (DC.slevel > this.params[0] && !this.DCstack.empty()) {
                            g.dispose();
                            DC = (WmfDecDC) this.DCstack.pop();
                            g = DC.gr;
                        }
                        break;
                }
                break;

            case META_PATBLT:
                e = (this.params[1] << 16) + this.params[0];
                if (log.isDebugEnabled()) {
                    log.debug("MetaPatBlt: ROP code=" + Integer.toString(e, 16));
                    log.debug(DC.aktbrush.getImage().toString());
                }
                a = DC.ytransfer(this.params[2]);
                b = DC.xtransfer(this.params[3]);
                c = DC.ytransfer(this.params[4]);
                d = DC.xtransfer(this.params[5]);
                switch (e) {
                    case WHITENESS:
                        g.setColor(Color.white); // <------ not yet debugged
                        g.fillRect(d, c, b, a);
                        break;
                    case BLACKNESS:
                        g.setColor(Color.black);
                        g.fillRect(d, c, b, a);
                        break;
                    case PATCOPY:
                        if ((im = DC.aktbrush.getImage()) != null) {
                            this.drawOpaqePattern(g, im, d, c, d + b, c + a, null);// fr);
                            // todo
                            log.debug("*** drawOpaqePattern");
                        } else {
                            g.setColor(DC.aktbrush.getColor());
                            g.fillRect(d, c, b, a);
                        }
                        break;
                    case PATINVERT:
                    case DSTINVERT:// FIXME
                    default:
                        log.warn("unsupported ROP code:" + Integer.toString(e, 16));
                }

                break;

            case META_STRETCHBLT:
                if (log.isDebugEnabled()) {
                    log.debug("MetaStretchBlt:" + rdSize);
                }
                e = (this.params[1] << 16) + this.params[0];
                a = DC.ytransfer(this.params[6]);
                b = DC.xtransfer(this.params[7]);
                c = DC.ytransfer(this.params[8]);
                d = DC.xtransfer(this.params[9]);
                switch (e) {
                    case WHITENESS:
                        g.setColor(Color.white); // <------ not yet debugged
                        g.fillRect(d, c, b, a);
                        break;
                    case BLACKNESS:
                        g.setColor(Color.black);
                        g.fillRect(d, c, b, a);
                        break;
                    case SRCCOPY:
                        im = this.OldBitmapImage(10, this.params);// ,fr);
                        if (im != null) {
                            g.drawImage(im, d, c, b, a, null);// fr); todo
                            log.debug("*** drawImage");
                            im = null;
                        } else if (this.drawCross_if_error) {
                            g.setColor(Color.black);
                            g.drawLine(0, 0, DC.xtransfer(this.params[7]), DC.ytransfer(this.params[6]));
                            g.drawLine(DC.xtransfer(this.params[7]), 0, 0, DC.ytransfer(this.params[6]));
                        }
                        break;
                    default:
                        log.warn("unsupported ROP code:" + Integer.toString(e, 16));
                }
                break;

            case META_DIBCREATEPATTERNBRUSH:
                if (log.isDebugEnabled()) {
                    log.debug("MetaDibCreatePatternBrush:" + this.params[0]);
                }
                im = this.DIBBitmapImage(2, this.params);// ,fr);
                if (im != null)
                    this.add_handle(new WmfDecObj(im));
                else
                    log.error("Error in MetaDibCreatePatternBrush");
                break;

            case META_DIBBITBLT:
            case META_STRETCHDIB:
            case META_DIBSTRETCHBLT:
                k = 0;
                switch (rdFunc) {
                    case META_DIBBITBLT:
                        k = -2; // 2 params less
                        if (log.isDebugEnabled()) {
                            log.debug("MetaDibBitBlt");
                        }
                        break;
                    case META_STRETCHDIB:
                        k = 1; // 1 param more
                        if (log.isDebugEnabled()) {
                            log.debug("MetaStretchDib");
                        }
                        break;
                    case META_DIBSTRETCHBLT:
                        k = 0;
                        if (log.isDebugEnabled()) {
                            log.debug("MetaDibStretchBlt");
                        }
                        break;
                }
                a = DC.ytransfer(this.params[6 + k]);
                b = DC.xtransfer(this.params[7 + k]);
                c = DC.ytransfer(this.params[8 + k]);
                d = DC.xtransfer(this.params[9 + k]);
                e = (this.params[1] << 16) + this.params[0];
                if (log.isDebugEnabled()) {
                    log.debug("dest X= " + d);
                    log.debug("dest Y= " + c);
                    log.debug("width = " + b);
                    log.debug("height= " + a);
                }
                switch (e) {
                    case WHITENESS:
                        g.setColor(Color.white); // <------ not yet debugged
                        g.fillRect(d, c, b, a);
                        break;
                    case BLACKNESS:
                        g.setColor(Color.black);
                        g.fillRect(d, c, b, a);
                        break;
                    case SRCCOPY:
                        im = this.DIBBitmapImage(10 + k, this.params);// ,fr); // here
                        // starts bmp
                        if (im != null) {
                            g.drawImage(im, d, c, b, a, null);// fr); todo
                            log.debug("*** drawImage");
                            im = null;
                        } else if (this.drawCross_if_error) { // draw a cross X
                            g.setColor(Color.black);
                            g.drawLine(d, c, d + b, c + a);
                            g.drawLine(d + b, c, d, c + a);
                        }
                        break;
                    default:
                        log.warn("unsupported ROP code:" + Integer.toString(e, 16));
                }
                break;

            case META_ESCAPE:
                switch (this.params[0]) {
                    case MFCOMMENT:
                        if (log.isDebugEnabled()) {
                            text = new byte[this.params[1]];
                            for (i = 0; i < this.params[1]; i++) {
                                if (i % 2 == 0)
                                    text[i] = (byte) this.getLoByteVal(this.params[i / 2 + 2]);
                                else
                                    text[i] = (byte) this.getHiByteVal(this.params[i / 2 + 2]);
                                if (text[i] == 0)
                                    break;
                            }
                            s = new String(text, 0);
                            log.debug("MetaEscape/MFCOMMENT: " + s);
                        }
                        break;
                    default:
                        if (log.isDebugEnabled()) {
                            log.debug("MetaEscape #" + this.params[0] + " "
                                    + ((this.params[1] + 1) >>> 2) + " Words");
                        }
                }
                break;

            case 0:
                return false; // EOF

            default:
                this.showparams(this.params, rdSize, rdFunc);
                break;
        }
        return true;
    }

    private void drawOpaqePattern(Graphics g, Image im, int x1, int y1, int x2, int y2,
                                  ImageObserver fr) {
        // it's just a little bit tricky ;-)
        // subsequent calls to clipRect allow only to make the clip-region
        // smaller
        // ... but not to set it to a different location that was not covered by
        // the
        // ... current clipping region. So we have to create a new graphics, g2
        int width = x2 - x1;
        int height = y2 - y1;
        int i, j;
        Graphics g2 = g.create(x1 - x1 % 8, y1 - y1 % 8, width + 8, height + 8);
        g2.clipRect(x1 % 8, y1 % 8, width, height);
        for (i = 0; i < width + 1; i += 8)
            for (j = 0; j < height + 1; j += 8)
                g2.drawImage(im, i, j, fr);
        g2.dispose();
    }

    private int getHiByteVal(int hhh) {
        byte b;
        if (hhh > 0)
            b = (byte) (hhh / 256);
        else {
            int iii = ~hhh;
            b = (byte) (iii >>> 8);
            b = (byte) ((byte) 255 - b);
        }
        return b < 0 ? b + 256 : b;
    }

    private int getLoByteVal(int hhh) {
        byte b;
        if (hhh > 0)
            b = (byte) (hhh % 256);
        else {
            int iii = ~hhh;
            b = (byte) (iii & 0xff);
            b = (byte) ((byte) 255 - b);
        }
        return b < 0 ? b + 256 : b;
    }

    private int transform(int param, int minsize) {
        int i = param;
        if (i < 0) {
            i = -i;
        }
        try {
            i = (i * this.res) / this.inch;
            if (i < minsize) {
                i = minsize;
            }
        } catch (ArithmeticException ex) {
        }
        return i / this.factor;
    }

    private void showparams(short[] params, int recSize, int Func) {
        log.debug("MetaRecord: " + Integer.toString(Func, 16) + " RecSize=" + recSize);
        log.debug("Data: ");
        for (int i = 0; i < recSize - 3; i++)
            if (i < 16)
                log.debug(Integer.toString(params[i], 16) + " ");
    }

    private int add_handle(WmfDecObj x) {
        int i;
        for (i = 0; i < this.obj; i++)
            if (this.gdiObj[i] == null) {
                this.gdiObj[i] = x;
                if (log.isDebugEnabled()) {
                    log.debug("Handle: " + i + "Obj: " + x);
                }
                return i;
            }
        return -1;
    }

    private int readInt32(InputStream in) throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        int ch3 = in.read();
        int ch4 = in.read();
        if ((ch1 | ch2 | ch3 | ch4) < 0)
            throw new EOFException();
        return (ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0);
    }

    private short readInt16(InputStream in) throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        if ((ch1 | ch2) < 0)
            throw new EOFException();
        return (short) ((ch2 << 8) + (ch1 << 0));
    }

    private int arcus(int ank, int geg) {
        int val = -(int) (Math.atan((float) (geg) / (float) (ank)) * 180 / Math.PI); // div
        // 0.0
        // is
        // "inf"
        if (ank < 0)
            val += 180;
        else if (geg >= 0)
            val += 360;
        return val;
    }

    // ----------- called by META_STRETCHDIB ----------- (1, 4, 8 bpp only, no
    // RLE) --------
    private Image DIBBitmapImage(int off, short params[])// ,Component comp)
    {
        int width = params[off + 2];
        int height = params[off + 4];
        int size = width * height;
        int bpp = params[off + 7];
        int colors, i, j, k, l, m, x = 0, startbitmap;
        if (params[off + 0] != 40 || params[off + 1] != 0) {
            log.warn("unsupported data format");
            return null;
        }
        if (params[off + 6] != 1) {
            log.warn("not supported: planes=" + params[off + 17]);
            return null;
        }
        if (bpp != 4 && bpp != 8 && bpp != 1 && bpp != 24) {
            log.warn("not supported: " + bpp + " bits per pixel");
            return null;
        }
        if (params[off + 8] != 0 || params[off + 9] != 0) {
            log.warn("not supported: RLE-compression");
            return null;
        }

        colors = 0;
        int palette[] = null;
        if (bpp <= 8) {
            colors = params[off + 16] != 0 ? params[off + 16] : (1 << bpp);
            palette = new int[colors];
            for (i = 0; i < colors; i++) { // palette data starts at
                // params[off+20]
                x = params[off + 21 + 2 * i]; // range = 0...255
                palette[i] = x << 16;
                x = this.getHiByteVal(params[off + 20 + 2 * i]);
                palette[i] += x << 8;
                x = this.getLoByteVal(params[off + 20 + 2 * i]);
                palette[i] += x;
                palette[i] -= 0x1000000; // add bit_pattern
                // (unsigned)0xFF000000 (for alpha)
            }
        }

        startbitmap = 20 + off + 2 * colors; // beginning after
        // header+palette data
        int pixels[] = new int[size];
        if (log.isDebugEnabled()) {
            log.debug("bpp = " + bpp);
        }
        switch (bpp) {
            case 1:
                for (k = height - 1, i = 0; k >= 0; k--)
                    for (l = 0; l < width; l += 16) {
                        i++;
                        m = params[i - 1 + startbitmap];
                        if (m < 0)
                            m += 65536;
                        for (j = 0, m = (m >> 8) | (m << 8); j + l < width && j < 16; j++) {
                            pixels[k * width + l + j] = (m & 0x8000) == 0 ? -0x1000000 : -0x1;
                            m <<= 1;
                        }
                        if (i % 2 != 0)
                            i++;
                    }
                break;

            case 4:
                for (k = height - 1, i = 0; k >= 0; k--)
                    for (l = 0; l < width; l++) {
                        switch (l % 4) {
                            case 0:
                                i++;
                                x = this.getLoByteVal(params[i - 1 + startbitmap]) >>> 4;
                                break;
                            case 1:
                                x = this.getLoByteVal(params[i - 1 + startbitmap]) & 0xf;
                                break;
                            case 2:
                                x = this.getHiByteVal(params[i - 1 + startbitmap]) >>> 4;
                                break;
                            case 3:
                                x = this.getHiByteVal(params[i - 1 + startbitmap]) & 0xf;
                                break;
                        }
                        pixels[k * width + l] = palette[x];
                    }
                break;

            case 8:
                for (k = height - 1, i = 0; k >= 0; k--) {
                    for (l = 0; l < width; l++) {
                        switch (l % 2) {
                            case 0:
                                i++;
                                x = this.getLoByteVal(params[i - 1 + startbitmap]);
                                break;
                            case 1:
                                x = this.getHiByteVal(params[i - 1 + startbitmap]);
                                break;
                        }
                        pixels[k * width + l] = palette[x];
                    }
                    if (i % 2 != 0) {
                        i++;
                    }
                }
                break;

            case 24:
                int ii = 4 - (size * 3) % 4;
                if (ii == 4) {
                    ii = 0;
                }
                boolean low = true;
                int count = 0;
                for (int jj = height - 1; jj >= 0; jj--) {
                    for (int kk = 0; kk < width; kk++) {
                        int ll;
                        if (low) {
                            count++;
                            ll = this.getLoByteVal(params[count - 1 + startbitmap]);
                            low = false;
                        } else {
                            ll = this.getHiByteVal(params[count - 1 + startbitmap]);
                            low = true;
                        }
                        int j1;
                        if (low) {
                            count++;
                            j1 = this.getLoByteVal(params[count - 1 + startbitmap]);
                            low = false;
                        } else {
                            j1 = this.getHiByteVal(params[count - 1 + startbitmap]);
                            low = true;
                        }
                        int k1;
                        if (low) {
                            count++;
                            k1 = this.getLoByteVal(params[count - 1 + startbitmap]);
                            low = false;
                        } else {
                            k1 = this.getHiByteVal(params[count - 1 + startbitmap]);
                            low = true;
                        }
                        pixels[jj * width + kk] = k1 << 16 & 0xff0000 | j1 << 8 & 0xff00 | ll & 0xff
                                | 0xff000000;
                    }

                    for (int i1 = 0; i1 < ii; i1++) {
                        if ((i1 % 2) == 0) {
                            count++;
                            low = false;
                        }
                    }

                }
                break;

        }
        // todo
        Image im = Toolkit.getDefaultToolkit().createImage(
                new MemoryImageSource(width, height, pixels, 0, width));
        log.info("*** Toolkit.getDefaultToolkit().createImage");
        pixels = null;
        return im;
    }

    // ----------- called by META_STRETCHBLT ----------- (here monochrome only)
    // --------
    private Image OldBitmapImage(int off, short params[])// ,Component comp)
    {
        int width = params[off];
        int height = params[off + 1];
        int i, j, k, l, m;
        if ((params[off + 3] != 1) || (params[off + 4] != 1)) {
            log.warn("sorry, the only supported format is: planes=1,bpp=1");
            return null;
        }
        int pixels[] = new int[width * height];
        for (k = 0, i = 0; k < height; k++)
            for (l = 0; l < width; l += 16) {
                m = params[off + 5 + i++];
                if (m < 0)
                    m += 65536;
                for (j = 0, m = (m >> 8) | (m << 8); j + l < width && j < 16; j++) {
                    pixels[k * width + l + j] = (m & 0x8000) == 0 ? -0x1000000 : -0x1;
                    m <<= 1;
                }
            }
        // todo
        Image im = Toolkit.getDefaultToolkit().createImage(
                new MemoryImageSource(width, height, pixels, 0, width));
        log.info("*** Toolkit.getDefaultToolkit().createImage");
        pixels = null;
        return im;
    }

    private final static int META_SETBKCOLOR = 0x0201; //
    private final static int META_SETBKMODE = 0x0102; //
    private final static int META_SETMAPMODE = 0x0103; //
    private final static int META_SETROP2 = 0x0104; //
    private final static int META_SETRELABS = 0x0105;
    private final static int META_SETPOLYFILLMODE = 0x0106; //
    private final static int META_SETSTRETCHBLTMODE = 0x0107;
    private final static int META_SETTEXTCHAREXTRA = 0x0108;
    private final static int META_SETTEXTCOLOR = 0x0209; //
    private final static int META_SETTEXTJUSTIFICATION = 0x020A; //
    private final static int META_SETWINDOWORG = 0x020B; //
    private final static int META_SETWINDOWEXT = 0x020C; //
    private final static int META_SETVIEWPORTORG = 0x020D;
    private final static int META_SETVIEWPORTEXT = 0x020E;
    private final static int META_OFFSETWINDOWORG = 0x020F;
    private final static int META_SCALEWINDOWEXT = 0x0410;
    private final static int META_OFFSETVIEWPORTORG = 0x0211;
    private final static int META_SCALEVIEWPORTEXT = 0x0412;
    private final static int META_LINETO = 0x0213; //
    private final static int META_MOVETO = 0x0214; //
    private final static int META_EXCLUDECLIPRECT = 0x0415;
    private final static int META_INTERSECTCLIPRECT = 0x0416;
    private final static int META_ARC = 0x0817; //
    private final static int META_ELLIPSE = 0x0418; //
    private final static int META_FLOODFILL = 0x0419;
    private final static int META_PIE = 0x081A; //
    private final static int META_RECTANGLE = 0x041B; //
    private final static int META_ROUNDRECT = 0x061C; //
    private final static int META_PATBLT = 0x061D; //
    private final static int META_SAVEDC = 0x001E;
    private final static int META_SETPIXEL = 0x041F; //
    private final static int META_OFFSETCLIPRGN = 0x0220;
    private final static int META_TEXTOUT = 0x0521; //
    private final static int META_BITBLT = 0x0922;
    private final static int META_STRETCHBLT = 0x0B23; //
    private final static int META_POLYGON = 0x0324; //
    private final static int META_POLYLINE = 0x0325; //
    private final static int META_ESCAPE = 0x0626; //
    private final static int META_RESTOREDC = 0x0127; //
    private final static int META_FILLREGION = 0x0228;
    private final static int META_FRAMEREGION = 0x0429;
    private final static int META_INVERTREGION = 0x012A;
    private final static int META_PAINTREGION = 0x012B;
    private final static int META_SELECTCLIPREGION = 0x012C; //
    private final static int META_SELECTOBJECT = 0x012D; //
    private final static int META_SETTEXTALIGN = 0x012E; //
    private final static int META_DRAWTEXT = 0x062F;
    private final static int META_CHORD = 0x0830; //
    private final static int META_SETMAPPERFLAGS = 0x0231;
    private final static int META_EXTTEXTOUT = 0x0A32; //
    private final static int META_SETDIBTODEV = 0x0D33;
    private final static int META_SELECTPALETTE = 0x0234; //
    private final static int META_REALIZEPALETTE = 0x0035; //
    private final static int META_ANIMATEPALETTE = 0x0436;
    private final static int META_SETPALENTRIES = 0x0037;
    private final static int META_POLYPOLYGON = 0x0538; //
    private final static int META_RESIZEPALETTE = 0x0139;
    private final static int META_DIBBITBLT = 0x0940; //
    private final static int META_DIBSTRETCHBLT = 0x0B41; //
    private final static int META_DIBCREATEPATTERNBRUSH = 0x0142; //
    private final static int META_STRETCHDIB = 0x0F43; //
    private final static int META_EXTFLOODFILL = 0x0548;
    private final static int META_RESETDC = 0x014C;
    private final static int META_STARTDOC = 0x014D;
    private final static int META_STARTPAGE = 0x004F;
    private final static int META_ENDPAGE = 0x0050;
    private final static int META_ABORTDOC = 0x0052;
    private final static int META_ENDDOC = 0x005E;
    private final static int META_DELETEOBJECT = 0x01F0; //
    private final static int META_CREATEPALETTE = 0x00F7; //
    private final static int META_CREATEBRUSH = 0x00F8;
    private final static int META_CREATEPATTERNBRUSH = 0x01F9;
    private final static int META_CREATEPENINDIRECT = 0x02FA; //
    private final static int META_CREATEFONTINDIRECT = 0x02FB; //
    private final static int META_CREATEBRUSHINDIRECT = 0x02FC; //
    private final static int META_CREATEBITMAPINDIRECT = 0x02FD;
    private final static int META_CREATEBITMAP = 0x06FE;
    private final static int META_CREATEREGION = 0x06FF; //

    private final static int MFCOMMENT = 15;
    private final static int SRCCOPY = 0xCC0020;
    private final static int PATCOPY = 0xF00021;
    private final static int PATINVERT = 0x5A0049;
    private final static int DSTINVERT = 0x550009;
    private final static int BLACKNESS = 0x000042;
    private final static int WHITENESS = 0xFF0062;
    private final static int BI_RLE8 = 1;
    private final static int BI_RLE4 = 2;

    private final static int TA_BASELINE = 24; // TextAlign options
    private final static int TA_BOTTOM = 8;
    private final static int TA_CENTER = 6;
    private final static int TA_UPDATECP = 1; // FIXME: update current postion
    final static int TA_TOP = 0;
    final static int OPAQUE = 2;
    final static int TRANSPARENT = 1;
    final static int ETO_GRAYED = 1;
    final static int ETO_OPAQUE = 2;
    final static int ETO_CLIPPED = 4;
    final static int PS_SOLID = 0;
    final static int PS_DASH = 1;
    final static int PS_DOT = 2;
    final static int PS_DASHDOT = 3;
    final static int PS_DASHDOTDOT = 4;
    final static int PS_NULL = 5;
    final static int PS_INSIDEFRAME = 6;
}

// this an all_in_one class of GDI-objects -------------------------------------

class WmfDecObj {
    /**
     * the default logger
     */
    private static final Logger log = LoggerFactory.getLogger(WmfDecObj.class);

    final static int M_PEN = 1;// the Windows GDI uses some other magic
    // words...
    final static int M_BRUSH = 2;
    final static int M_FONT = 3;
    final static int M_BITMAP = 4;
    final static int M_CLIP = 5;
    final static int M_PALETTE = 6;

    private Color c;
    private Font f;
    private boolean f_underl;
    private int f_orient;
    private Rectangle r;
    private int magic;
    private Image ibrush;
    private int hatch;
    private int p_style;

    WmfDecObj(Color cc, int mm) {
        this.c = cc;
        this.magic = mm;
    }

    WmfDecObj(int penattr, Color cc) {
        this.c = cc;
        this.magic = M_PEN;
        this.p_style = penattr;
    }

    WmfDecObj(Font ff, int underlined, int orientation) {
        this.f = ff;
        this.f_underl = underlined == 0 ? false : true;
        this.f_orient = orientation;
        this.magic = M_FONT;
    }

    WmfDecObj(Image ii) {
        this.ibrush = ii;
        this.c = null;
        this.magic = M_BRUSH;
    }

    WmfDecObj(int hatchstyle, Color cc, Color back)// ,Component fr)
    {
        this.c = cc; // TRANSPARENT mode not suppd
        this.hatch = hatchstyle;
        this.ibrush = this.createOpaqueImage(hatchstyle, cc, back);// ,fr);
        this.magic = M_BRUSH;
    }

    WmfDecObj(int left, int top, int right, int bottom) {
        this.r = new Rectangle(left, top, right - left, bottom - top);
        this.magic = M_CLIP;
    }

    Color getColor() {
        return this.c;
    }

    Image getImage() {
        return this.ibrush; // draw a hatch image if OPAQUE mode ?
    }

    Font getFont() {
        return this.f;
    }

    boolean isUnderlined() {
        return this.f_underl;
    }

    int getFontOrientation() {
        return this.f_orient;
    }

    int getPenStyle() {
        return this.p_style;
    }

    Rectangle getRect() {
        return this.r;
    }

    // int getHatch()
    // {
    // return hatch;
    // }

    int getMagic() {
        return this.magic;
    }

    Image createOpaqueImage(int hatchstyle, Color cc, Color back)// ,Component
            // fr)
    {
        Image im;
        int i, pixels[] = new int[64];
        // from HS_HORIZONTAL=0 up to HS_DIAGCROSS=5
        int set[][] = { { 32, 33, 34, 35, 36, 37, 38, 39 }, { 4, 12, 20, 28, 36, 44, 52, 60 },
                        { 0, 9, 18, 27, 36, 45, 54, 63 }, { 7, 14, 21, 28, 35, 42, 49, 56 },
                        { 32, 33, 34, 35, 36, 37, 38, 39, 4, 12, 20, 28, 44, 52, 60 },
                        { 0, 9, 18, 27, 36, 45, 54, 63, 7, 14, 21, 28, 35, 42, 49, 56 } };

        for (i = 0; i < 64; i++)
            pixels[i] = /* back.getRGB(); */Color.white.getRGB();
        try {
            for (i = 0; i < set[hatchstyle].length; i++) {
                pixels[set[hatchstyle][i]] = cc.getRGB();
            }
            MemoryImageSource mis = new MemoryImageSource(8, 8, ColorModel.getRGBdefault(), pixels,
                    0, 8);
            // todo: correct?
            im = Toolkit.getDefaultToolkit().createImage(mis);
            log.info("*** Toolkit.getDefaultToolkit().createImage");
            mis = null;
        } catch (ArrayIndexOutOfBoundsException e) {
            im = null;
            log.warn("unknown hatchstyle found.");
        }
        return im;
    }

}

// Consider: this is NOT really what Windows-GDI does!

class WmfDecDC implements Cloneable {
    WmfDecDC(int extX, int extY, int orgX, int orgY) {
        // init (some metafiles don't call META_SETWINDOWEXT/META_SETWINDOWORG
        // !)
        this.winextX = (short) (this.truewidth = extX);
        this.winextY = (short) (this.trueheight = extY);
        this.winorgX = (short) orgX;
        this.winorgY = (short) orgY;
        this.aktclip = new WmfDecObj(this.winorgX, this.winorgY, this.winextX, this.winextY);
        this.aktpen = new WmfDecObj(WmfDecoder.PS_SOLID, Color.black);
        this.aktbrush = new WmfDecObj(Color.white, WmfDecObj.M_BRUSH);
        this.aktpal = new WmfDecObj(Color.white, WmfDecObj.M_PALETTE);
        this.aktbmp = new WmfDecObj(Color.white, WmfDecObj.M_BITMAP);
        this.aktfont = new WmfDecObj(new Font("Courier", Font.PLAIN, 12), 0, 0);
    }

    // this is our "device context" (DC), init'd with GDI defaults
    // it has 6 selectable objects
    public WmfDecObj aktpen, aktbrush, aktpal, aktbmp, aktclip, aktfont;

    public Color akttextc = Color.black;
    public Color aktbackgnd = Color.white; // for usage in NULL-pens and
    // -brushs
    public int aktYpos = 0; // current graphic cursor (MoveTo,LineTo)
    public int aktXpos = 0;
    public short winextX = (short) 1; // for SetWindowExt()
    public short winextY = (short) 1;
    public short winorgX = (short) 0; // for SetWindowOrg()
    public short winorgY = (short) 0;
    public int slevel = 0; // SaveDC-level
    public int akttextalign = WmfDecoder.TA_TOP;
    public int aktbkmode = WmfDecoder.OPAQUE;
    public Graphics gr;

    // --------- still missing: ROP, ViewPort,MapMode,PolyfillMode etc.

    private int trueheight, truewidth; // not part of GDI device context

    int ytransfer(short coo) {
        int icoo = coo;
        icoo -= this.winorgY;
        icoo *= this.trueheight;
        return icoo / this.winextY;
    }

    int xtransfer(short coo) {
        int icoo = coo;
        icoo -= this.winorgX;
        icoo *= this.truewidth;
        return icoo / this.winextX;
    }

    public Object clone() // not yet ready
    {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        } // this never happens?
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy