You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CTM.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.area;
  18. import java.awt.Rectangle;
  19. import java.awt.geom.Rectangle2D;
  20. import java.io.Serializable;
  21. import org.apache.fop.datatypes.FODimension;
  22. import org.apache.fop.fo.Constants;
  23. /**
  24. * Describe a PDF or PostScript style coordinate transformation matrix (CTM).
  25. * The matrix encodes translations, scaling and rotations of the coordinate
  26. * system used to render pages.
  27. */
  28. public class CTM implements Serializable {
  29. private double a, b, c, d, e, f;
  30. private static final CTM CTM_LRTB = new CTM(1, 0, 0, 1, 0, 0);
  31. private static final CTM CTM_RLTB = new CTM(-1, 0, 0, 1, 0, 0);
  32. private static final CTM CTM_TBRL = new CTM(0, 1, -1, 0, 0, 0);
  33. /**
  34. * Create the identity matrix
  35. */
  36. public CTM() {
  37. a = 1;
  38. b = 0;
  39. c = 0;
  40. d = 1;
  41. e = 0;
  42. f = 0;
  43. }
  44. /**
  45. * Initialize a CTM from the passed arguments.
  46. *
  47. * @param a the x scale
  48. * @param b the x shear
  49. * @param c the y shear
  50. * @param d the y scale
  51. * @param e the x shift
  52. * @param f the y shift
  53. */
  54. public CTM(double a, double b, double c, double d, double e, double f) {
  55. this.a = a;
  56. this.b = b;
  57. this.c = c;
  58. this.d = d;
  59. this.e = e;
  60. this.f = f;
  61. }
  62. /**
  63. * Initialize a CTM to the identity matrix with a translation
  64. * specified by x and y
  65. *
  66. * @param x the x shift
  67. * @param y the y shift.
  68. */
  69. public CTM(double x, double y) {
  70. this.a = 1;
  71. this.b = 0;
  72. this.c = 0;
  73. this.d = 1;
  74. this.e = x;
  75. this.f = y;
  76. }
  77. /**
  78. * Initialize a CTM with the values of another CTM.
  79. *
  80. * @param ctm another CTM
  81. */
  82. protected CTM(CTM ctm) {
  83. this.a = ctm.a;
  84. this.b = ctm.b;
  85. this.c = ctm.c;
  86. this.d = ctm.d;
  87. this.e = ctm.e;
  88. this.f = ctm.f;
  89. }
  90. /**
  91. * Return a CTM which will transform coordinates for a particular writing-mode
  92. * into normalized first quandrant coordinates.
  93. * @param wm A writing mode constant from fo.properties.WritingMode, ie.
  94. * one of LR_TB, RL_TB, TB_RL.
  95. * @param ipd The inline-progression dimension of the reference area whose
  96. * CTM is being set..
  97. * @param bpd The block-progression dimension of the reference area whose
  98. * CTM is being set.
  99. * @return a new CTM with the required transform
  100. */
  101. public static CTM getWMctm(int wm, int ipd, int bpd) {
  102. CTM wmctm;
  103. switch (wm) {
  104. case Constants.WritingMode.LR_TB:
  105. return new CTM(CTM_LRTB);
  106. case Constants.WritingMode.RL_TB: {
  107. wmctm = new CTM(CTM_RLTB);
  108. wmctm.e = ipd;
  109. return wmctm;
  110. }
  111. //return CTM_RLTB.translate(ipd, 0);
  112. case Constants.WritingMode.TB_RL: { // CJK
  113. wmctm = new CTM(CTM_TBRL);
  114. wmctm.e = bpd;
  115. return wmctm;
  116. }
  117. //return CTM_TBRL.translate(0, ipd);
  118. default:
  119. return null;
  120. }
  121. }
  122. /**
  123. * Multiply new passed CTM with this one and generate a new result CTM.
  124. * @param premult The CTM to multiply with this one. The new one will be
  125. * the first multiplicand.
  126. * @return CTM The result of multiplying premult * this.
  127. */
  128. public CTM multiply(CTM premult) {
  129. CTM rslt = new CTM ((premult.a * a) + (premult.b * c),
  130. (premult.a * b) + (premult.b * d),
  131. (premult.c * a) + (premult.d * c),
  132. (premult.c * b) + (premult.d * d),
  133. (premult.e * a) + (premult.f * c) + e,
  134. (premult.e * b) + (premult.f * d) + f);
  135. return rslt;
  136. }
  137. /**
  138. * Rotate this CTM by "angle" radians and return a new result CTM.
  139. * This is used to account for reference-orientation.
  140. * @param angle The angle in radians. Positive angles are measured counter-
  141. * clockwise.
  142. * @return CTM The result of rotating this CTM.
  143. */
  144. public CTM rotate(double angle) {
  145. double cos, sin;
  146. if (angle == 90.0) {
  147. cos = 0.0;
  148. sin = 1.0;
  149. } else if (angle == 270.0) {
  150. cos = 0.0;
  151. sin = -1.0;
  152. } else if (angle == 180.0) {
  153. cos = -1.0;
  154. sin = 0.0;
  155. } else {
  156. double rad = Math.toRadians(angle);
  157. cos = Math.cos(rad);
  158. sin = Math.sin(rad);
  159. }
  160. CTM rotate = new CTM(cos, -sin, sin, cos, 0, 0);
  161. return multiply(rotate);
  162. }
  163. /**
  164. * Translate this CTM by the passed x and y values and return a new result CTM.
  165. * @param x The amount to translate along the x axis.
  166. * @param y The amount to translate along the y axis.
  167. * @return CTM The result of translating this CTM.
  168. */
  169. public CTM translate(double x, double y) {
  170. CTM translate = new CTM(1, 0, 0, 1, x, y);
  171. return multiply(translate);
  172. }
  173. /**
  174. * Scale this CTM by the passed x and y values and return a new result CTM.
  175. * @param x The amount to scale along the x axis.
  176. * @param y The amount to scale along the y axis.
  177. * @return CTM The result of scaling this CTM.
  178. */
  179. public CTM scale(double x, double y) {
  180. CTM scale = new CTM(x, 0, 0, y, 0, 0);
  181. return multiply(scale);
  182. }
  183. /**
  184. * Transform a rectangle by the CTM to produce a rectangle in the transformed
  185. * coordinate system.
  186. * @param inRect The rectangle in the original coordinate system
  187. * @return Rectangle2D The rectangle in the transformed coordinate system.
  188. */
  189. public Rectangle2D transform(Rectangle2D inRect) {
  190. // Store as 2 sets of 2 points and transform those, then
  191. // recalculate the width and height
  192. int x1t = (int)(inRect.getX() * a + inRect.getY() * c + e);
  193. int y1t = (int)(inRect.getX() * b + inRect.getY() * d + f);
  194. int x2t = (int)((inRect.getX() + inRect.getWidth()) * a
  195. + (inRect.getY() + inRect.getHeight()) * c + e);
  196. int y2t = (int)((inRect.getX() + inRect.getWidth()) * b
  197. + (inRect.getY() + inRect.getHeight()) * d + f);
  198. // Normalize with x1 < x2
  199. if (x1t > x2t) {
  200. int tmp = x2t;
  201. x2t = x1t;
  202. x1t = tmp;
  203. }
  204. if (y1t > y2t) {
  205. int tmp = y2t;
  206. y2t = y1t;
  207. y1t = tmp;
  208. }
  209. return new Rectangle(x1t, y1t, x2t - x1t, y2t - y1t);
  210. }
  211. /**
  212. * Get string for this transform.
  213. *
  214. * @return a string with the transform values
  215. */
  216. public String toString() {
  217. return "[" + a + " " + b + " " + c + " " + d + " " + e + " "
  218. + f + "]";
  219. }
  220. /**
  221. * Get an array containing the values of this transform.
  222. * This creates and returns a new transform with the values in it.
  223. *
  224. * @return an array containing the transform values
  225. */
  226. public double[] toArray() {
  227. return new double[]{a, b, c, d, e, f};
  228. }
  229. /**
  230. * Construct a coordinate transformation matrix (CTM).
  231. * @param absVPrect absolute viewpoint rectangle
  232. * @param reldims relative dimensions
  233. * @return CTM the coordinate transformation matrix (CTM)
  234. */
  235. public static CTM getCTMandRelDims(int absRefOrient,
  236. int writingMode,
  237. Rectangle2D absVPrect,
  238. FODimension reldims) {
  239. int width, height;
  240. // We will use the absolute reference-orientation to set up the CTM.
  241. // The value here is relative to its ancestor reference area.
  242. if (absRefOrient % 180 == 0) {
  243. width = (int) absVPrect.getWidth();
  244. height = (int) absVPrect.getHeight();
  245. } else {
  246. // invert width and height since top left are rotated by 90 (cl or ccl)
  247. height = (int) absVPrect.getWidth();
  248. width = (int) absVPrect.getHeight();
  249. }
  250. /* Set up the CTM for the content of this reference area.
  251. * This will transform region content coordinates in
  252. * writing-mode relative into absolute page-relative
  253. * which will then be translated based on the position of
  254. * the region viewport.
  255. * (Note: scrolling between region vp and ref area when
  256. * doing online content!)
  257. */
  258. CTM ctm = new CTM(absVPrect.getX(), absVPrect.getY());
  259. // First transform for rotation
  260. if (absRefOrient != 0) {
  261. // Rotation implies translation to keep the drawing area in the
  262. // first quadrant. Note: rotation is counter-clockwise
  263. switch (absRefOrient) {
  264. case 90:
  265. ctm = ctm.translate(0, width); // width = absVPrect.height
  266. break;
  267. case 180:
  268. ctm = ctm.translate(width, height);
  269. break;
  270. case 270:
  271. ctm = ctm.translate(height, 0); // height = absVPrect.width
  272. break;
  273. }
  274. ctm = ctm.rotate(absRefOrient);
  275. }
  276. /* Since we've already put adjusted width and height values for the
  277. * top and left positions implied by the reference-orientation, we
  278. * can set ipd and bpd appropriately based on the writing mode.
  279. */
  280. if (writingMode == Constants.WritingMode.LR_TB || writingMode == Constants.WritingMode.RL_TB) {
  281. reldims.ipd = width;
  282. reldims.bpd = height;
  283. } else {
  284. reldims.ipd = height;
  285. reldims.bpd = width;
  286. }
  287. // Set a rectangle to be the writing-mode relative version???
  288. // Now transform for writing mode
  289. return ctm.multiply(CTM.getWMctm(writingMode, reldims.ipd, reldims.bpd));
  290. }
  291. }