blob: 80172be8b46cad6ad35eaf0044288b7f286369fd [file] [log] [blame]
/*
* 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$ */
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));
}
}