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

com.itextpdf.kernel.pdf.canvas.wmf.MetaDo Maven / Gradle / Ivy

There is a newer version: 9.0.0
Show newest version
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2024 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see .
 */
package com.itextpdf.kernel.pdf.canvas.wmf;

import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.io.image.ImageType;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.colors.Color;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.font.PdfFontFactory.EmbeddingStrategy;
import com.itextpdf.kernel.geom.Point;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.canvas.PdfCanvasConstants;
import com.itextpdf.kernel.pdf.xobject.PdfImageXObject;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.List;

/**
 * A class to process WMF files. Used internally by {@link com.itextpdf.kernel.pdf.canvas.wmf.WmfImageHelper}.
 */
public class MetaDo {

    public static final int META_SETBKCOLOR            = 0x0201;
    public static final int META_SETBKMODE             = 0x0102;
    public static final int META_SETMAPMODE            = 0x0103;
    public static final int META_SETROP2               = 0x0104;
    public static final int META_SETRELABS             = 0x0105;
    public static final int META_SETPOLYFILLMODE       = 0x0106;
    public static final int META_SETSTRETCHBLTMODE     = 0x0107;
    public static final int META_SETTEXTCHAREXTRA      = 0x0108;
    public static final int META_SETTEXTCOLOR          = 0x0209;
    public static final int META_SETTEXTJUSTIFICATION  = 0x020A;
    public static final int META_SETWINDOWORG          = 0x020B;
    public static final int META_SETWINDOWEXT          = 0x020C;
    public static final int META_SETVIEWPORTORG        = 0x020D;
    public static final int META_SETVIEWPORTEXT        = 0x020E;
    public static final int META_OFFSETWINDOWORG       = 0x020F;
    public static final int META_SCALEWINDOWEXT        = 0x0410;
    public static final int META_OFFSETVIEWPORTORG     = 0x0211;
    public static final int META_SCALEVIEWPORTEXT      = 0x0412;
    public static final int META_LINETO                = 0x0213;
    public static final int META_MOVETO                = 0x0214;
    public static final int META_EXCLUDECLIPRECT       = 0x0415;
    public static final int META_INTERSECTCLIPRECT     = 0x0416;
    public static final int META_ARC                   = 0x0817;
    public static final int META_ELLIPSE               = 0x0418;
    public static final int META_FLOODFILL             = 0x0419;
    public static final int META_PIE                   = 0x081A;
    public static final int META_RECTANGLE             = 0x041B;
    public static final int META_ROUNDRECT             = 0x061C;
    public static final int META_PATBLT                = 0x061D;
    public static final int META_SAVEDC                = 0x001E;
    public static final int META_SETPIXEL              = 0x041F;
    public static final int META_OFFSETCLIPRGN         = 0x0220;
    public static final int META_TEXTOUT               = 0x0521;
    public static final int META_BITBLT                = 0x0922;
    public static final int META_STRETCHBLT            = 0x0B23;
    public static final int META_POLYGON               = 0x0324;
    public static final int META_POLYLINE              = 0x0325;
    public static final int META_ESCAPE                = 0x0626;
    public static final int META_RESTOREDC             = 0x0127;
    public static final int META_FILLREGION            = 0x0228;
    public static final int META_FRAMEREGION           = 0x0429;
    public static final int META_INVERTREGION          = 0x012A;
    public static final int META_PAINTREGION           = 0x012B;
    public static final int META_SELECTCLIPREGION      = 0x012C;
    public static final int META_SELECTOBJECT          = 0x012D;
    public static final int META_SETTEXTALIGN          = 0x012E;
    public static final int META_CHORD                 = 0x0830;
    public static final int META_SETMAPPERFLAGS        = 0x0231;
    public static final int META_EXTTEXTOUT            = 0x0a32;
    public static final int META_SETDIBTODEV           = 0x0d33;
    public static final int META_SELECTPALETTE         = 0x0234;
    public static final int META_REALIZEPALETTE        = 0x0035;
    public static final int META_ANIMATEPALETTE        = 0x0436;
    public static final int META_SETPALENTRIES         = 0x0037;
    public static final int META_POLYPOLYGON           = 0x0538;
    public static final int META_RESIZEPALETTE         = 0x0139;
    public static final int META_DIBBITBLT             = 0x0940;
    public static final int META_DIBSTRETCHBLT         = 0x0b41;
    public static final int META_DIBCREATEPATTERNBRUSH = 0x0142;
    public static final int META_STRETCHDIB            = 0x0f43;
    public static final int META_EXTFLOODFILL          = 0x0548;
    public static final int META_DELETEOBJECT          = 0x01f0;
    public static final int META_CREATEPALETTE         = 0x00f7;
    public static final int META_CREATEPATTERNBRUSH    = 0x01F9;
    public static final int META_CREATEPENINDIRECT     = 0x02FA;
    public static final int META_CREATEFONTINDIRECT    = 0x02FB;
    public static final int META_CREATEBRUSHINDIRECT   = 0x02FC;
    public static final int META_CREATEREGION          = 0x06FF;

    /**
     * PdfCanvas of the MetaDo object.
     */
    public PdfCanvas cb;

    /**
     * The InputMeta instance containing the data.
     */
    public InputMeta in;

    int left;
    int top;
    int right;
    int bottom;
    int inch;
    MetaState state = new MetaState();

    /**
     * Creates a MetaDo instance.
     *
     * @param in inputstream containing the data
     * @param cb PdfCanvas
     */
    public MetaDo(InputStream in, PdfCanvas cb) {
        this.cb = cb;
        this.in = new InputMeta(in);
    }

    /**
     * Reads and processes all the data of the InputMeta.
     *
     * @throws IOException an {@link IOException}
     */
    public void readAll() throws IOException {
        if (in.readInt() != 0x9AC6CDD7) {
            throw new PdfException(KernelExceptionMessageConstant.NOT_A_PLACEABLE_WINDOWS_METAFILE);
        }
        in.readWord();
        left = in.readShort();
        top = in.readShort();
        right = in.readShort();
        bottom = in.readShort();
        inch = in.readWord();
        state.setScalingX((float)(right - left) / (float)inch * 72f);
        state.setScalingY((float)(bottom - top) / (float)inch * 72f);
        state.setOffsetWx(left);
        state.setOffsetWy(top);
        state.setExtentWx(right - left);
        state.setExtentWy(bottom - top);
        in.readInt();
        in.readWord();
        in.skip(18);

        int tsize;
        int function;
        cb.setLineCapStyle(PdfCanvasConstants.LineCapStyle.ROUND);
        cb.setLineJoinStyle(PdfCanvasConstants.LineJoinStyle.ROUND);
        for (;;) {
            int lenMarker = in.getLength();
            tsize = in.readInt();
            if (tsize < 3)
                break;
            function = in.readWord();
            switch (function) {
                case 0:
                    break;
                case META_CREATEPALETTE:
                case META_CREATEREGION:
                case META_DIBCREATEPATTERNBRUSH:
                    state.addMetaObject(new MetaObject());
                    break;
                case META_CREATEPENINDIRECT:
                {
                    MetaPen pen = new MetaPen();
                    pen.init(in);
                    state.addMetaObject(pen);
                    break;
                }
                case META_CREATEBRUSHINDIRECT:
                {
                    MetaBrush brush = new MetaBrush();
                    brush.init(in);
                    state.addMetaObject(brush);
                    break;
                }
                case META_CREATEFONTINDIRECT:
                {
                    MetaFont font = new MetaFont();
                    font.init(in);
                    state.addMetaObject(font);
                    break;
                }
                case META_SELECTOBJECT:
                {
                    int idx = in.readWord();
                    state.selectMetaObject(idx, cb);
                    break;
                }
                case META_DELETEOBJECT:
                {
                    int idx = in.readWord();
                    state.deleteMetaObject(idx);
                    break;
                }
                case META_SAVEDC:
                    state.saveState(cb);
                    break;
                case META_RESTOREDC:
                {
                    int idx = in.readShort();
                    state.restoreState(idx, cb);
                    break;
                }
                case META_SETWINDOWORG:
                    state.setOffsetWy(in.readShort());
                    state.setOffsetWx(in.readShort());
                    break;
                case META_SETWINDOWEXT:
                    state.setExtentWy(in.readShort());
                    state.setExtentWx(in.readShort());
                    break;
                case META_MOVETO:
                {
                    int y = in.readShort();
                    Point p = new Point(in.readShort(), y);
                    state.setCurrentPoint(p);
                    break;
                }
                case META_LINETO:
                {
                    int y = in.readShort();
                    int x = in.readShort();
                    Point p = state.getCurrentPoint();
                    cb.moveTo(state.transformX((int)p.getX()), state.transformY((int)p.getY()));
                    cb.lineTo(state.transformX(x), state.transformY(y));
                    cb.stroke();
                    state.setCurrentPoint(new Point(x, y));
                    break;
                }
                case META_POLYLINE:
                {
                    state.setLineJoinPolygon(cb);
                    int len = in.readWord();
                    int x = in.readShort();
                    int y = in.readShort();
                    cb.moveTo(state.transformX(x), state.transformY(y));
                    for (int k = 1; k < len; ++k) {
                        x = in.readShort();
                        y = in.readShort();
                        cb.lineTo(state.transformX(x), state.transformY(y));
                    }
                    cb.stroke();
                    break;
                }
                case META_POLYGON:
                {
                    if (isNullStrokeFill(false))
                        break;
                    int len = in.readWord();
                    int sx = in.readShort();
                    int sy = in.readShort();
                    cb.moveTo(state.transformX(sx), state.transformY(sy));
                    for (int k = 1; k < len; ++k) {
                        int x = in.readShort();
                        int y = in.readShort();
                        cb.lineTo(state.transformX(x), state.transformY(y));
                    }
                    cb.lineTo(state.transformX(sx), state.transformY(sy));
                    strokeAndFill();
                    break;
                }
                case META_POLYPOLYGON:
                {
                    if (isNullStrokeFill(false))
                        break;
                    int numPoly = in.readWord();
                    int[] lens = new int[numPoly];
                    for (int k = 0; k < lens.length; ++k)
                        lens[k] = in.readWord();
                    for (int j = 0; j < lens.length; ++j) {
                        int len = lens[j];
                        int sx = in.readShort();
                        int sy = in.readShort();
                        cb.moveTo(state.transformX(sx), state.transformY(sy));
                        for (int k = 1; k < len; ++k) {
                            int x = in.readShort();
                            int y = in.readShort();
                            cb.lineTo(state.transformX(x), state.transformY(y));
                        }
                        cb.lineTo(state.transformX(sx), state.transformY(sy));
                    }
                    strokeAndFill();
                    break;
                }
                case META_ELLIPSE:
                {
                    if (isNullStrokeFill(state.getLineNeutral()))
                        break;
                    int b = in.readShort();
                    int r = in.readShort();
                    int t = in.readShort();
                    int l = in.readShort();
                    cb.arc(state.transformX(l), state.transformY(b), state.transformX(r), state.transformY(t), 0, 360);
                    strokeAndFill();
                    break;
                }
                case META_ARC:
                {
                    if (isNullStrokeFill(state.getLineNeutral()))
                        break;
                    float yend = state.transformY(in.readShort());
                    float xend = state.transformX(in.readShort());
                    float ystart = state.transformY(in.readShort());
                    float xstart = state.transformX(in.readShort());
                    float b = state.transformY(in.readShort());
                    float r = state.transformX(in.readShort());
                    float t = state.transformY(in.readShort());
                    float l = state.transformX(in.readShort());
                    float cx = (r + l) / 2;
                    float cy = (t + b) / 2;
                    float arc1 = getArc(cx, cy, xstart, ystart);
                    float arc2 = getArc(cx, cy, xend, yend);
                    arc2 -= arc1;
                    if (arc2 <= 0)
                        arc2 += 360;
                    cb.arc(l, b, r, t, arc1, arc2);
                    cb.stroke();
                    break;
                }
                case META_PIE:
                {
                    if (isNullStrokeFill(state.getLineNeutral()))
                        break;
                    float yend = state.transformY(in.readShort());
                    float xend = state.transformX(in.readShort());
                    float ystart = state.transformY(in.readShort());
                    float xstart = state.transformX(in.readShort());
                    float b = state.transformY(in.readShort());
                    float r = state.transformX(in.readShort());
                    float t = state.transformY(in.readShort());
                    float l = state.transformX(in.readShort());
                    float cx = (r + l) / 2;
                    float cy = (t + b) / 2;
                    float arc1 = getArc(cx, cy, xstart, ystart);
                    float arc2 = getArc(cx, cy, xend, yend);
                    arc2 -= arc1;
                    if (arc2 <= 0)
                        arc2 += 360;
                    List ar = PdfCanvas.bezierArc(l, b, r, t, arc1, arc2);
                    if (ar.size() == 0)
                        break;
                    double[] pt = ar.get(0);
                    cb.moveTo(cx, cy);
                    cb.lineTo(pt[0], pt[1]);
                    for (int k = 0; k < ar.size(); ++k) {
                        pt = ar.get(k);
                        cb.curveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]);
                    }
                    cb.lineTo(cx, cy);
                    strokeAndFill();
                    break;
                }
                case META_CHORD:
                {
                    if (isNullStrokeFill(state.getLineNeutral()))
                        break;
                    float yend = state.transformY(in.readShort());
                    float xend = state.transformX(in.readShort());
                    float ystart = state.transformY(in.readShort());
                    float xstart = state.transformX(in.readShort());
                    float b = state.transformY(in.readShort());
                    float r = state.transformX(in.readShort());
                    float t = state.transformY(in.readShort());
                    float l = state.transformX(in.readShort());
                    float cx = (r + l) / 2;
                    float cy = (t + b) / 2;
                    float arc1 = getArc(cx, cy, xstart, ystart);
                    float arc2 = getArc(cx, cy, xend, yend);
                    arc2 -= arc1;
                    if (arc2 <= 0)
                        arc2 += 360;
                    List ar = PdfCanvas.bezierArc(l, b, r, t, arc1, arc2);
                    if (ar.size() == 0)
                        break;
                    double[] pt = ar.get(0);
                    cx = (float)pt[0];
                    cy = (float)pt[1];
                    cb.moveTo(cx, cy);
                    for (int k = 0; k < ar.size(); ++k) {
                        pt = ar.get(k);
                        cb.curveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]);
                    }
                    cb.lineTo(cx, cy);
                    strokeAndFill();
                    break;
                }
                case META_RECTANGLE:
                {
                    if (isNullStrokeFill(true))
                        break;
                    float b = state.transformY(in.readShort());
                    float r = state.transformX(in.readShort());
                    float t = state.transformY(in.readShort());
                    float l = state.transformX(in.readShort());
                    cb.rectangle(l, b, r - l, t - b);
                    strokeAndFill();
                    break;
                }
                case META_ROUNDRECT:
                {
                    if (isNullStrokeFill(true))
                        break;
                    float h = state.transformY(0) - state.transformY(in.readShort());
                    float w = state.transformX(in.readShort()) - state.transformX(0);
                    float b = state.transformY(in.readShort());
                    float r = state.transformX(in.readShort());
                    float t = state.transformY(in.readShort());
                    float l = state.transformX(in.readShort());
                    cb.roundRectangle(l, b, r - l, t - b, (h + w) / 4);
                    strokeAndFill();
                    break;
                }
                case META_INTERSECTCLIPRECT:
                {
                    float b = state.transformY(in.readShort());
                    float r = state.transformX(in.readShort());
                    float t = state.transformY(in.readShort());
                    float l = state.transformX(in.readShort());
                    cb.rectangle(l, b, r - l, t - b);
                    cb.eoClip();
                    cb.endPath();
                    break;
                }
                case META_EXTTEXTOUT:
                {
                    int y = in.readShort();
                    int x = in.readShort();
                    int count = in.readWord();
                    int flag = in.readWord();
                    int x1 = 0;
                    int y1 = 0;
                    int x2 = 0;
                    int y2 = 0;
                    if ((flag & (MetaFont.ETO_CLIPPED | MetaFont.ETO_OPAQUE)) != 0) {
                        x1 = in.readShort();
                        y1 = in.readShort();
                        x2 = in.readShort();
                        y2 = in.readShort();
                    }
                    byte[] text = new byte[count];
                    int k;
                    for (k = 0; k < count; ++k) {
                        byte c = (byte)in.readByte();
                        if (c == 0)
                            break;
                        text[k] = c;
                    }
                    String s;
                    try {
                        s = new String(text, 0, k, "Cp1252");
                    }
                    catch (UnsupportedEncodingException e) {
                        s = new String(text, 0, k);
                    }
                    outputText(x, y, flag, x1, y1, x2, y2, s);
                    break;
                }
                case META_TEXTOUT:
                {
                    int count = in.readWord();
                    byte[] text = new byte[count];
                    int k;
                    for (k = 0; k < count; ++k) {
                        byte c = (byte)in.readByte();
                        if (c == 0)
                            break;
                        text[k] = c;
                    }
                    String s;
                    try {
                        s = new String(text, 0, k, "Cp1252");
                    }
                    catch (UnsupportedEncodingException e) {
                        s = new String(text, 0, k);
                    }
                    count = count + 1 & 0xfffe;
                    in.skip(count - k);
                    int y = in.readShort();
                    int x = in.readShort();
                    outputText(x, y, 0, 0, 0, 0, 0, s);
                    break;
                }
                case META_SETBKCOLOR:
                    state.setCurrentBackgroundColor(in.readColor());
                    break;
                case META_SETTEXTCOLOR:
                    state.setCurrentTextColor(in.readColor());
                    break;
                case META_SETTEXTALIGN:
                    state.setTextAlign(in.readWord());
                    break;
                case META_SETBKMODE:
                    state.setBackgroundMode(in.readWord());
                    break;
                case META_SETPOLYFILLMODE:
                    state.setPolyFillMode(in.readWord());
                    break;
                case META_SETPIXEL:
                {
                    Color color = in.readColor();
                    int y = in.readShort();
                    int x = in.readShort();
                    cb.saveState();
                    cb.setFillColor(color);
                    cb.rectangle(state.transformX(x), state.transformY(y), .2f, .2f);
                    cb.fill();
                    cb.restoreState();
                    break;
                }
                case META_DIBSTRETCHBLT:
                case META_STRETCHDIB: {
                    int rop = in.readInt();
                    if (function == META_STRETCHDIB) {
                        /*int usage = */ in.readWord();
                    }
                    int srcHeight = in.readShort();
                    int srcWidth = in.readShort();
                    int ySrc = in.readShort();
                    int xSrc = in.readShort();
                    float destHeight = state.transformY(in.readShort()) - state.transformY(0);
                    float destWidth = state.transformX(in.readShort()) - state.transformX(0);
                    float yDest = state.transformY(in.readShort());
                    float xDest = state.transformX(in.readShort());
                    byte[] b = new byte[tsize * 2 - (in.getLength() - lenMarker)];
                    for (int k = 0; k < b.length; ++k)
                        b[k] = (byte)in.readByte();
                    try {
                        cb.saveState();
                        cb.rectangle(xDest, yDest, destWidth, destHeight);
                        cb.clip();
                        cb.endPath();
                        ImageData bmpImage = ImageDataFactory.createBmp(b, true);
                        PdfImageXObject imageXObject = new PdfImageXObject(bmpImage);

                        float width = destWidth * bmpImage.getWidth() / srcWidth;
                        float height = -destHeight * bmpImage.getHeight() / srcHeight;
                        float x = xDest - destWidth * xSrc / srcWidth;
                        float y = yDest + destHeight * ySrc / srcHeight - height;
                        cb.addXObjectFittedIntoRectangle(imageXObject, new Rectangle(x, y, width, height));
                        cb.restoreState();
                    }
                    catch (Exception e) {
                        // empty on purpose
                    }
                    break;
                }
            }
            in.skip(tsize * 2 - (in.getLength() - lenMarker));
        }
        state.cleanup(cb);
    }

    /**
     * Output Text at a certain x and y coordinate. Clipped or opaque text isn't supported as of yet.
     *
     * @param x x-coordinate
     * @param y y-coordinate
     * @param flag flag indicating clipped or opaque
     * @param x1 x1-coordinate of the rectangle if clipped or opaque
     * @param y1 y1-coordinate of the rectangle if clipped or opaque
     * @param x2 x2-coordinate of the rectangle if clipped or opaque
     * @param y2 y1-coordinate of the rectangle if clipped or opaque
     * @param text text to output
     * @throws IOException an {@link IOException}
     */
    public void outputText(int x, int y, int flag, int x1, int y1, int x2, int y2, String text) throws IOException {

        MetaFont font = state.getCurrentFont();
        float refX = state.transformX(x);
        float refY = state.transformY(y);
        float angle = state.transformAngle(font.getAngle());
        float sin = (float)Math.sin(angle);
        float cos = (float)Math.cos(angle);
        float fontSize = font.getFontSize(state);
        FontProgram fp = font.getFont();
        int align = state.getTextAlign();
        // NOTE, MetaFont always creates with CP1252 encoding.
        int normalizedWidth = 0;
        byte[] bytes = font.encoding.convertToBytes(text);
        for (byte b : bytes) {
            normalizedWidth += fp.getWidth(0xff & b);
        }
        final float textWidth = FontProgram.convertTextSpaceToGlyphSpace(fontSize) * normalizedWidth;
        float tx = 0;
        float ty = 0;
        float descender = fp.getFontMetrics().getTypoDescender();
        float ury = fp.getFontMetrics().getBbox()[3];
        cb.saveState();
        cb.concatMatrix(cos, sin, -sin, cos, refX, refY);
        if ((align & MetaState.TA_CENTER) == MetaState.TA_CENTER) {
            tx = -textWidth / 2;
        } else if ((align & MetaState.TA_RIGHT) == MetaState.TA_RIGHT) {
            tx = -textWidth;
        } if ((align & MetaState.TA_BASELINE) == MetaState.TA_BASELINE) {
            ty = 0;
        } else if ((align & MetaState.TA_BOTTOM) == MetaState.TA_BOTTOM) {
            ty = -descender;
        } else {
            ty = -ury;
        }
        Color textColor;
        if (state.getBackgroundMode() == MetaState.OPAQUE) {
            textColor = state.getCurrentBackgroundColor();
            cb.setFillColor(textColor);
            cb.rectangle(tx, ty + descender, textWidth, ury - descender);
            cb.fill();
        }
        textColor = state.getCurrentTextColor();
        cb.setFillColor(textColor);
        cb.beginText();
        cb.setFontAndSize(PdfFontFactory.createFont(state.getCurrentFont().getFont(),
                PdfEncodings.CP1252, EmbeddingStrategy.PREFER_EMBEDDED), fontSize);
        cb.setTextMatrix(tx, ty);
        cb.showText(text);
        cb.endText();
        if (font.isUnderline()) {
            cb.rectangle(tx, ty - fontSize / 4, textWidth, fontSize / 15);
            cb.fill();
        }
        if (font.isStrikeout()) {
            cb.rectangle(tx, ty + fontSize / 3, textWidth, fontSize / 15);
            cb.fill();
        }
        cb.restoreState();
    }

    /**
     * Return true if the pen style is null and if it isn't a brush.
     *
     * @param isRectangle value to decide how to change the state. If true state.setLineJoinRectangle(cb) is called,
     *                    if false state.setLineJoinPolygon(cb) is called.
     * @return true if the pen style is null and if it isn't a brush
     */
    public boolean isNullStrokeFill(boolean isRectangle) {
        MetaPen pen = state.getCurrentPen();
        MetaBrush brush = state.getCurrentBrush();
        boolean noPen = pen.getStyle() == MetaPen.PS_NULL;
        int style = brush.getStyle();
        boolean isBrush = style == MetaBrush.BS_SOLID || style == MetaBrush.BS_HATCHED && state.getBackgroundMode() == MetaState.OPAQUE;
        boolean result = noPen && !isBrush;
        if (!noPen) {
            if (isRectangle)
                state.setLineJoinRectangle(cb);
            else
                state.setLineJoinPolygon(cb);
        }
        return result;
    }

    /**
     * Stroke and fill the MetaPen and MetaBrush paths.
     */
    public void strokeAndFill() {
        MetaPen pen = state.getCurrentPen();
        MetaBrush brush = state.getCurrentBrush();
        int penStyle = pen.getStyle();
        int brushStyle = brush.getStyle();
        if (penStyle == MetaPen.PS_NULL) {
            cb.closePath();
            if (state.getPolyFillMode() == MetaState.ALTERNATE) {
                cb.eoFill();
            }
            else {
                cb.fill();
            }
        }
        else {
            boolean isBrush = brushStyle == MetaBrush.BS_SOLID || brushStyle == MetaBrush.BS_HATCHED && state.getBackgroundMode() == MetaState.OPAQUE;
            if (isBrush) {
                if (state.getPolyFillMode() == MetaState.ALTERNATE)
                    cb.closePathEoFillStroke();
                else
                    cb.closePathFillStroke();
            }
            else {
                cb.closePathStroke();
            }
        }
    }

    static float getArc(float xCenter, float yCenter, float xDot, float yDot) {
        double s = Math.atan2(yDot - yCenter, xDot - xCenter);
        if (s < 0)
            s += Math.PI * 2;
        return (float)(s / Math.PI * 180);
    }

    /**
     * Wrap a BMP image in an WMF.
     *
     * @param image the BMP image to be wrapped
     * @return the wrapped BMP
     * @throws IOException an {@link IOException}
     */
    public static byte[] wrapBMP(ImageData image) throws IOException {
        if (image.getOriginalType() != ImageType.BMP) {
            throw new PdfException(KernelExceptionMessageConstant.ONLY_BMP_CAN_BE_WRAPPED_IN_WMF);
        }
        InputStream imgIn;
        byte data[];
        if (image.getData() == null) {
            imgIn = image.getUrl().openStream();
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int b = 0;
            while ((b = imgIn.read()) != -1)
                out.write(b);
            imgIn.close();
            data = out.toByteArray();
        } else {
            data = image.getData();
        }
        int sizeBmpWords = data.length - 14 + 1 >>> 1;
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        // write metafile header
        writeWord(os, 1);
        writeWord(os, 9);
        writeWord(os, 0x0300);
        // total metafile size
        writeDWord(os, 9 + 4 + 5 + 5 + 13 + sizeBmpWords + 3);
        writeWord(os, 1);
        // max record size
        writeDWord(os, 14 + sizeBmpWords);
        writeWord(os, 0);
        // write records
        writeDWord(os, 4);
        writeWord(os, META_SETMAPMODE);
        writeWord(os, 8);

        writeDWord(os, 5);
        writeWord(os, META_SETWINDOWORG);
        writeWord(os, 0);
        writeWord(os, 0);

        writeDWord(os, 5);
        writeWord(os, META_SETWINDOWEXT);
        writeWord(os, (int)image.getHeight());
        writeWord(os, (int)image.getWidth());

        writeDWord(os, 13 + sizeBmpWords);
        writeWord(os, META_DIBSTRETCHBLT);
        writeDWord(os, 0x00cc0020);
        writeWord(os, (int)image.getHeight());
        writeWord(os, (int)image.getWidth());
        writeWord(os, 0);
        writeWord(os, 0);
        writeWord(os, (int)image.getHeight());
        writeWord(os, (int)image.getWidth());
        writeWord(os, 0);
        writeWord(os, 0);
        os.write(data, 14, data.length - 14);
        if ((data.length & 1) == 1) {
            os.write(0);
        }
        writeDWord(os, 3);
        writeWord(os, 0);
        os.close();
        return os.toByteArray();
    }

    /**
     * Writes the specified value to the specified outputstream as a word.
     *
     * @param os outputstream to write the word to
     * @param v value to be written
     * @throws IOException an {@link IOException}
     */
    public static void writeWord(OutputStream os, int v) throws IOException {
        os.write(v & 0xff);
        os.write(v >>> 8 & 0xff);
    }

    /**
     * Writes the specified value to the specified outputstream as a dword.
     *
     * @param os outputstream to write the dword to
     * @param v value to be written
     * @throws IOException an {@link IOException}
     */
    public static void writeDWord(OutputStream os, int v) throws IOException {
        writeWord(os, v & 0xffff);
        writeWord(os, v >>> 16 & 0xffff);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy