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

org.apache.fop.area.CTM Maven / Gradle / Ivy

Go to download

Tool to convert CSV and XLS to XML, to transform XML and to convert XML to CSV, HTML, other text files, PDF etc., useful as command line tool and integrated in other projects.

There is a newer version: 3.119
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id: CTM.java 1479969 2013-05-07 16:23:13Z vhennebert $ */

package org.apache.fop.area;

import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;

import org.apache.fop.datatypes.FODimension;
import org.apache.fop.traits.WritingMode;

import static org.apache.fop.fo.Constants.EN_LR_TB;
import static org.apache.fop.fo.Constants.EN_RL_TB;
import static org.apache.fop.fo.Constants.EN_TB_LR;
import static org.apache.fop.fo.Constants.EN_TB_RL;

/**
 * Describe a PDF or PostScript style coordinate transformation matrix (CTM).
 * The matrix encodes translations, scaling and rotations of the coordinate
 * system used to render pages.
 */
public class CTM implements Serializable {

    private static final long serialVersionUID = -8743287485623778341L;

    private double a;
    private double b;
    private double c;
    private double d;
    private double e;
    private double f;

    private static final CTM CTM_LRTB = new CTM(1, 0, 0, 1, 0, 0);
    private static final CTM CTM_RLTB = new CTM(1, 0, 0, 1, 0, 0);
    private static final CTM CTM_TBRL = new CTM(0, 1, -1, 0, 0, 0);

    /**
     * Create the identity matrix
     */
    public CTM() {
        a = 1;
        b = 0;
        c = 0;
        d = 1;
        e = 0;
        f = 0;
    }

    /**
     * Initialize a CTM from the passed arguments.
     *
     * @param a the x scale
     * @param b the x shear
     * @param c the y shear
     * @param d the y scale
     * @param e the x shift
     * @param f the y shift
     */
    public CTM(double a, double b, double c, double d, double e, double f) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
        this.e = e;
        this.f = f;
    }

    /**
     * Initialize a CTM to the identity matrix with a translation
     * specified by x and y
     *
     * @param x the x shift
     * @param y the y shift.
     */
    public CTM(double x, double y) {
        this.a = 1;
        this.b = 0;
        this.c = 0;
        this.d = 1;
        this.e = x;
        this.f = y;
    }

    /**
     * Initialize a CTM with the values of another CTM.
     *
     * @param ctm another CTM
     */
    protected CTM(CTM ctm) {
        this.a = ctm.a;
        this.b = ctm.b;
        this.c = ctm.c;
        this.d = ctm.d;
        this.e = ctm.e;
        this.f = ctm.f;
    }

    /**
     * Initialize a CTM with the values of an AffineTransform.
     *
     * @param at the transformation matrix
     */
    public CTM(AffineTransform at) {
        double[] matrix = new double[6];
        at.getMatrix(matrix);
        this.a = matrix[0];
        this.b = matrix[1];
        this.c = matrix[2];
        this.d = matrix[3];
        this.e = matrix[4];
        this.f = matrix[5];
    }

    /**
     * Return a CTM which will transform coordinates for a particular writing-mode
     * into normalized first quandrant coordinates.
     * @param wm A writing mode constant from fo.properties.WritingMode, ie.
     * one of LR_TB, RL_TB, TB_RL, TB_LR.
     * @param ipd The inline-progression dimension of the reference area whose
     * CTM is being set..
     * @param bpd The block-progression dimension of the reference area whose
     * CTM is being set.
     * @return a new CTM with the required transform
     */
    public static CTM getWMctm(WritingMode wm, int ipd, int bpd) {
        CTM wmctm;
        switch (wm.getEnumValue()) {
            case EN_LR_TB:
                return new CTM(CTM_LRTB);
            case EN_RL_TB:
                return new CTM(CTM_RLTB);
            case EN_TB_RL:  // CJK
            case EN_TB_LR:  // CJK
                wmctm = new CTM(CTM_TBRL);
                wmctm.e = bpd;
                return wmctm;
            default:
                return null;
        }
    }

    /**
     * Multiply new passed CTM with this one and generate a new result CTM.
     * @param premult The CTM to multiply with this one. The new one will be
     * the first multiplicand.
     * @return CTM The result of multiplying premult * this.
     */
    public CTM multiply(CTM premult) {
        return new CTM((premult.a * a) + (premult.b * c),
                              (premult.a * b) + (premult.b * d),
                              (premult.c * a) + (premult.d * c),
                              (premult.c * b) + (premult.d * d),
                              (premult.e * a) + (premult.f * c) + e,
                              (premult.e * b) + (premult.f * d) + f);
    }

    /**
     * Rotate this CTM by "angle" radians and return a new result CTM.
     * This is used to account for reference-orientation.
     * @param angle The angle in radians. Positive angles are measured counter-
     * clockwise.
     * @return CTM The result of rotating this CTM.
     */
    public CTM rotate(double angle) {
        double cos;
        double sin;
        if (angle == 90.0 || angle == -270.0) {
            cos = 0.0;
            sin = 1.0;
        } else if (angle == 270.0 || angle == -90.0) {
            cos = 0.0;
            sin = -1.0;
        } else if (angle == 180.0 || angle == -180.0) {
            cos = -1.0;
            sin = 0.0;
        } else {
            double rad = Math.toRadians(angle);
            cos = Math.cos(rad);
            sin = Math.sin(rad);
        }
        CTM rotate = new CTM(cos, -sin, sin, cos, 0, 0);
        return multiply(rotate);
    }

    /**
     * Translate this CTM by the passed x and y values and return a new result CTM.
     * @param x The amount to translate along the x axis.
     * @param y The amount to translate along the y axis.
     * @return CTM The result of translating this CTM.
     */
    public CTM translate(double x, double y) {
        CTM translate = new CTM(1, 0, 0, 1, x, y);
        return multiply(translate);
    }

    /**
     * Scale this CTM by the passed x and y values and return a new result CTM.
     * @param x The amount to scale along the x axis.
     * @param y The amount to scale along the y axis.
     * @return CTM The result of scaling this CTM.
     */
    public CTM scale(double x, double y) {
        CTM scale = new CTM(x, 0, 0, y, 0, 0);
        return multiply(scale);
    }

    /**
     * Transform a rectangle by the CTM to produce a rectangle in the transformed
     * coordinate system.
     * @param inRect The rectangle in the original coordinate system
     * @return Rectangle2D The rectangle in the transformed coordinate system.
     */
    public Rectangle2D transform(Rectangle2D inRect) {
        // Store as 2 sets of 2 points and transform those, then
        // recalculate the width and height
        int x1t = (int)(inRect.getX() * a + inRect.getY() * c + e);
        int y1t = (int)(inRect.getX() * b + inRect.getY() * d + f);
        int x2t = (int)((inRect.getX() + inRect.getWidth()) * a
                        + (inRect.getY() + inRect.getHeight()) * c + e);
        int y2t = (int)((inRect.getX() + inRect.getWidth()) * b
                        + (inRect.getY() + inRect.getHeight()) * d + f);
        // Normalize with x1 < x2
        if (x1t > x2t) {
            int tmp = x2t;
            x2t = x1t;
            x1t = tmp;
        }
        if (y1t > y2t) {
            int tmp = y2t;
            y2t = y1t;
            y1t = tmp;
        }
        return new Rectangle(x1t, y1t, x2t - x1t, y2t - y1t);
    }

    /**
     * Get string for this transform.
     *
     * @return a string with the transform values
     */
    @Override
    public String toString() {
        return "[" + a + " " + b + " " + c + " " + d + " " + e + " "
               + f + "]";
    }

    /**
     * Get an array containing the values of this transform.
     * This creates and returns a new transform with the values in it.
     *
     * @return an array containing the transform values
     */
    public double[] toArray() {
        return new double[]{a, b, c, d, e, f};
    }

    /**
     * Returns this CTM as an AffineTransform object.
     * @return the AffineTransform representation
     */
    public AffineTransform toAffineTransform() {
        return new AffineTransform(toArray());
    }

    /**
     * Construct a coordinate transformation matrix (CTM).
     * @param absRefOrient absolute reference orientation
     * @param writingMode the writing mode
     * @param absVPrect absolute viewpoint rectangle
     * @param reldims relative dimensions
     * @return CTM the coordinate transformation matrix (CTM)
     */
    public static CTM getCTMandRelDims(int absRefOrient,
                                       WritingMode writingMode,
                                       Rectangle2D absVPrect,
                                       FODimension reldims) {
        int width;
        int height;
        // We will use the absolute reference-orientation to set up the CTM.
        // The value here is relative to its ancestor reference area.
        if (absRefOrient % 180 == 0) {
            width = (int) absVPrect.getWidth();
            height = (int) absVPrect.getHeight();
        } else {
            // invert width and height since top left are rotated by 90 (cl or ccl)
            height = (int) absVPrect.getWidth();
            width = (int) absVPrect.getHeight();
        }
        /* Set up the CTM for the content of this reference area.
         * This will transform region content coordinates in
         * writing-mode relative into absolute page-relative
         * which will then be translated based on the position of
         * the region viewport.
         * (Note: scrolling between region vp and ref area when
         * doing online content!)
         */
        CTM ctm = new CTM(absVPrect.getX(), absVPrect.getY());

        // First transform for rotation
        if (absRefOrient != 0) {
            // Rotation implies translation to keep the drawing area in the
            // first quadrant. Note: rotation is counter-clockwise
            switch (absRefOrient) {
                case 90:
                case -270:
                    ctm = ctm.translate(0, width); // width = absVPrect.height
                    break;
                case 180:
                case -180:
                    ctm = ctm.translate(width, height);
                    break;
                case 270:
                case -90:
                    ctm = ctm.translate(height, 0); // height = absVPrect.width
                    break;
                default:
                    throw new RuntimeException();
            }
            ctm = ctm.rotate(absRefOrient);
        }
        /* Since we've already put adjusted width and height values for the
         * top and left positions implied by the reference-orientation, we
         * can set ipd and bpd appropriately based on the writing mode.
         */

        switch (writingMode.getEnumValue()) {
        case EN_TB_LR:
        case EN_TB_RL:
            reldims.ipd = height;
            reldims.bpd = width;
            break;
        case EN_LR_TB:
        case EN_RL_TB:
        default:
            reldims.ipd = width;
            reldims.bpd = height;
            break;
        }
        // Set a rectangle to be the writing-mode relative version???
        // Now transform for writing mode
        return ctm.multiply(CTM.getWMctm(writingMode, reldims.ipd, reldims.bpd));
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy