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.

AbstractPaintingState.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.util;
  19. import java.awt.Color;
  20. import java.awt.geom.AffineTransform;
  21. import java.io.Serializable;
  22. import java.util.Arrays;
  23. import java.util.Collection;
  24. import java.util.Iterator;
  25. import java.util.List;
  26. import java.util.Stack;
  27. /**
  28. * A base class which holds information about the current painting state.
  29. */
  30. public abstract class AbstractPaintingState implements Cloneable, Serializable {
  31. private static final long serialVersionUID = 5998356138437094188L;
  32. /** current state data */
  33. private AbstractData data = null;
  34. /** the state stack */
  35. private StateStack/*<AbstractData>*/ stateStack = new StateStack/*<AbstractData>*/();
  36. /**
  37. * Instantiates a new state data object
  38. *
  39. * @return a new state data object
  40. */
  41. protected abstract AbstractData instantiateData();
  42. /**
  43. * Instantiates a new state object
  44. *
  45. * @return a new state object
  46. */
  47. protected abstract AbstractPaintingState instantiate();
  48. /**
  49. * Returns the currently valid state
  50. *
  51. * @return the currently valid state
  52. */
  53. public AbstractData getData() {
  54. if (data == null) {
  55. data = instantiateData();
  56. }
  57. return data;
  58. }
  59. /**
  60. * Set the current color.
  61. * Check if the new color is a change and then set the current color.
  62. *
  63. * @param col the color to set
  64. * @return true if the color has changed
  65. */
  66. public boolean setColor(Color col) {
  67. Color other = getData().color;
  68. if (!org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor(col, other)) {
  69. getData().color = col;
  70. return true;
  71. }
  72. return false;
  73. }
  74. /**
  75. * Get the color.
  76. *
  77. * @return the color
  78. */
  79. public Color getColor() {
  80. if (getData().color == null) {
  81. getData().color = Color.black;
  82. }
  83. return getData().color;
  84. }
  85. /**
  86. * Get the background color.
  87. *
  88. * @return the background color
  89. */
  90. public Color getBackColor() {
  91. if (getData().backColor == null) {
  92. getData().backColor = Color.white;
  93. }
  94. return getData().backColor;
  95. }
  96. /**
  97. * Set the current background color.
  98. * Check if the new background color is a change and then set the current background color.
  99. *
  100. * @param col the background color to set
  101. * @return true if the color has changed
  102. */
  103. public boolean setBackColor(Color col) {
  104. Color other = getData().backColor;
  105. if (!org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor(col, other)) {
  106. getData().backColor = col;
  107. return true;
  108. }
  109. return false;
  110. }
  111. /**
  112. * Set the current font name
  113. *
  114. * @param internalFontName the internal font name
  115. * @return true if the font name has changed
  116. */
  117. public boolean setFontName(String internalFontName) {
  118. if (!internalFontName.equals(getData().fontName)) {
  119. getData().fontName = internalFontName;
  120. return true;
  121. }
  122. return false;
  123. }
  124. /**
  125. * Gets the current font name
  126. *
  127. * @return the current font name
  128. */
  129. public String getFontName() {
  130. return getData().fontName;
  131. }
  132. /**
  133. * Gets the current font size
  134. *
  135. * @return the current font size
  136. */
  137. public int getFontSize() {
  138. return getData().fontSize;
  139. }
  140. /**
  141. * Set the current font size.
  142. * Check if the font size is a change and then set the current font size.
  143. *
  144. * @param size the font size to set
  145. * @return true if the font size has changed
  146. */
  147. public boolean setFontSize(int size) {
  148. if (size != getData().fontSize) {
  149. getData().fontSize = size;
  150. return true;
  151. }
  152. return false;
  153. }
  154. /**
  155. * Set the current line width.
  156. *
  157. * @param width the line width in points
  158. * @return true if the line width has changed
  159. */
  160. public boolean setLineWidth(float width) {
  161. if (getData().lineWidth != width) {
  162. getData().lineWidth = width;
  163. return true;
  164. }
  165. return false;
  166. }
  167. /**
  168. * Returns the current line width
  169. *
  170. * @return the current line width
  171. */
  172. public float getLineWidth() {
  173. return getData().lineWidth;
  174. }
  175. /**
  176. * Sets the dash array (line type) for the current basic stroke
  177. *
  178. * @param dash the line dash array
  179. * @return true if the dash array has changed
  180. */
  181. public boolean setDashArray(float[] dash) {
  182. if (!Arrays.equals(dash, getData().dashArray)) {
  183. getData().dashArray = dash;
  184. return true;
  185. }
  186. return false;
  187. }
  188. /**
  189. * Get the current transform.
  190. * This gets the combination of all transforms in the
  191. * current state.
  192. *
  193. * @return the calculate combined transform for the current state
  194. */
  195. public AffineTransform getTransform() {
  196. AffineTransform at = new AffineTransform();
  197. for (Iterator iter = stateStack.iterator(); iter.hasNext();) {
  198. AbstractData data = (AbstractData)iter.next();
  199. AffineTransform stackTrans = data.getTransform();
  200. at.concatenate(stackTrans);
  201. }
  202. AffineTransform currentTrans = getData().getTransform();
  203. at.concatenate(currentTrans);
  204. return at;
  205. }
  206. /**
  207. * Check the current transform.
  208. * The transform for the current state is the combination of all
  209. * transforms in the current state. The parameter is compared
  210. * against this current transform.
  211. *
  212. * @param tf the transform the check against
  213. * @return true if the new transform is different then the current transform
  214. */
  215. public boolean checkTransform(AffineTransform tf) {
  216. return !tf.equals(getData().getTransform());
  217. }
  218. /**
  219. * Get a copy of the base transform for the page. Used to translate
  220. * IPP/BPP values into X,Y positions when positioning is "fixed".
  221. *
  222. * @return the base transform, or null if the state stack is empty
  223. */
  224. public AffineTransform getBaseTransform() {
  225. if (stateStack.isEmpty()) {
  226. return null;
  227. } else {
  228. AbstractData baseData = (AbstractData)stateStack.get(0);
  229. return (AffineTransform) baseData.getTransform().clone();
  230. }
  231. }
  232. /**
  233. * Concatenates the given AffineTransform to the current one.
  234. *
  235. * @param at the transform to concatenate to the current level transform
  236. */
  237. public void concatenate(AffineTransform at) {
  238. getData().concatenate(at);
  239. }
  240. /**
  241. * Resets the current AffineTransform to the Base AffineTransform.
  242. */
  243. public void resetTransform() {
  244. getData().setTransform(getBaseTransform());
  245. }
  246. /**
  247. * Clears the current AffineTransform to the Identity AffineTransform
  248. */
  249. public void clearTransform() {
  250. getData().clearTransform();
  251. }
  252. /**
  253. * Save the current painting state.
  254. * This pushes the current painting state onto the stack.
  255. * This call should be used when the Q operator is used
  256. * so that the state is known when popped.
  257. */
  258. public void save() {
  259. AbstractData copy = (AbstractData)getData().clone();
  260. stateStack.push(copy);
  261. }
  262. /**
  263. * Restore the current painting state.
  264. * This pops the painting state from the stack and sets current values to popped state.
  265. *
  266. * @return the restored state, null if the stack is empty
  267. */
  268. public AbstractData restore() {
  269. if (!stateStack.isEmpty()) {
  270. setData((AbstractData)stateStack.pop());
  271. return this.data;
  272. } else {
  273. return null;
  274. }
  275. }
  276. /**
  277. * Save all painting state data.
  278. * This pushes all painting state data in the given list to the stack
  279. *
  280. * @param dataList a state data list
  281. */
  282. public void saveAll(List/*<AbstractData>*/ dataList) {
  283. Iterator it = dataList.iterator();
  284. while (it.hasNext()) {
  285. // save current data on stack
  286. save();
  287. setData((AbstractData)it.next());
  288. }
  289. }
  290. /**
  291. * Restore all painting state data.
  292. * This pops all painting state data from the stack
  293. *
  294. * @return a list of state data popped from the stack
  295. */
  296. public List/*<AbstractData>*/ restoreAll() {
  297. List/*<AbstractData>*/ dataList = new java.util.ArrayList/*<AbstractData>*/();
  298. AbstractData data;
  299. while (true) {
  300. data = getData();
  301. if (restore() == null) {
  302. break;
  303. }
  304. // insert because of stack-popping
  305. dataList.add(0, data);
  306. }
  307. return dataList;
  308. }
  309. /**
  310. * Sets the current state data
  311. *
  312. * @param currentData state data
  313. */
  314. protected void setData(AbstractData data) {
  315. this.data = data;
  316. }
  317. /**
  318. * Clears the state stack
  319. */
  320. public void clear() {
  321. stateStack.clear();
  322. setData(null);
  323. }
  324. /**
  325. * Return the state stack
  326. *
  327. * @return the state stack
  328. */
  329. protected Stack/*<AbstractData>*/ getStateStack() {
  330. return this.stateStack;
  331. }
  332. /** {@inheritDoc} */
  333. @Override
  334. public Object clone() {
  335. AbstractPaintingState state = instantiate();
  336. state.stateStack = new StateStack(this.stateStack);
  337. state.data = (AbstractData)this.data.clone();
  338. return state;
  339. }
  340. /** {@inheritDoc} */
  341. @Override
  342. public String toString() {
  343. return ", stateStack=" + stateStack
  344. + ", currentData=" + data;
  345. }
  346. /**
  347. * A stack implementation which holds state objects
  348. */
  349. public class StateStack extends java.util.Stack {
  350. private static final long serialVersionUID = 4897178211223823041L;
  351. /**
  352. * Default constructor
  353. */
  354. public StateStack() {
  355. super();
  356. }
  357. /**
  358. * Copy constructor
  359. *
  360. * @param c initial contents of stack
  361. */
  362. public StateStack(Collection c) {
  363. elementCount = c.size();
  364. // 10% for growth
  365. elementData = new Object[
  366. (int)Math.min((elementCount * 110L) / 100, Integer.MAX_VALUE)];
  367. c.toArray(elementData);
  368. }
  369. }
  370. /**
  371. * A base painting state data holding object
  372. */
  373. public abstract class AbstractData implements Cloneable, Serializable {
  374. private static final long serialVersionUID = 5208418041189828624L;
  375. /** The current color */
  376. protected Color color = null;
  377. /** The current background color */
  378. protected Color backColor = null;
  379. /** The current font name */
  380. protected String fontName = null;
  381. /** The current font size */
  382. protected int fontSize = 0;
  383. /** The current line width */
  384. protected float lineWidth = 0;
  385. /** The dash array for the current basic stroke (line type) */
  386. protected float[] dashArray = null;
  387. /** The current transform */
  388. protected AffineTransform transform = null;
  389. /**
  390. * Returns a newly create data object
  391. *
  392. * @return a new data object
  393. */
  394. protected abstract AbstractData instantiate();
  395. /**
  396. * Concatenate the given AffineTransform with the current thus creating
  397. * a new viewport. Note that all concatenation operations are logged
  398. * so they can be replayed if necessary (ex. for block-containers with
  399. * "fixed" positioning.
  400. *
  401. * @param at Transformation to perform
  402. */
  403. public void concatenate(AffineTransform at) {
  404. getTransform().concatenate(at);
  405. }
  406. /**
  407. * Get the current AffineTransform.
  408. *
  409. * @return the current transform
  410. */
  411. public AffineTransform getTransform() {
  412. if (transform == null) {
  413. transform = new AffineTransform();
  414. }
  415. return transform;
  416. }
  417. /**
  418. * Sets the current AffineTransform.
  419. */
  420. public void setTransform(AffineTransform baseTransform) {
  421. this.transform = baseTransform;
  422. }
  423. /**
  424. * Resets the current AffineTransform.
  425. */
  426. public void clearTransform() {
  427. transform = new AffineTransform();
  428. }
  429. /**
  430. * Returns the derived rotation from the current transform
  431. *
  432. * @return the derived rotation from the current transform
  433. */
  434. public int getDerivedRotation() {
  435. AffineTransform at = getTransform();
  436. double sx = at.getScaleX();
  437. double sy = at.getScaleY();
  438. double shx = at.getShearX();
  439. double shy = at.getShearY();
  440. int rotation = 0;
  441. if (sx == 0 && sy == 0 && shx > 0 && shy < 0) {
  442. rotation = 270;
  443. } else if (sx < 0 && sy < 0 && shx == 0 && shy == 0) {
  444. rotation = 180;
  445. } else if (sx == 0 && sy == 0 && shx < 0 && shy > 0) {
  446. rotation = 90;
  447. } else {
  448. rotation = 0;
  449. }
  450. return rotation;
  451. }
  452. /** {@inheritDoc} */
  453. @Override
  454. public Object clone() {
  455. AbstractData data = instantiate();
  456. data.color = this.color;
  457. data.backColor = this.backColor;
  458. data.fontName = this.fontName;
  459. data.fontSize = this.fontSize;
  460. data.lineWidth = this.lineWidth;
  461. data.dashArray = this.dashArray;
  462. if (this.transform == null) {
  463. this.transform = new AffineTransform();
  464. }
  465. data.transform = new AffineTransform(this.transform);
  466. return data;
  467. }
  468. /** {@inheritDoc} */
  469. @Override
  470. public String toString() {
  471. return "color=" + color
  472. + ", backColor=" + backColor
  473. + ", fontName=" + fontName
  474. + ", fontSize=" + fontSize
  475. + ", lineWidth=" + lineWidth
  476. + ", dashArray=" + dashArray
  477. + ", transform=" + transform;
  478. }
  479. }
  480. }