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.

ImageLayout.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  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.layoutmgr.inline;
  19. import java.awt.Dimension;
  20. import java.awt.Rectangle;
  21. import org.apache.commons.logging.Log;
  22. import org.apache.commons.logging.LogFactory;
  23. import org.apache.fop.datatypes.Length;
  24. import org.apache.fop.datatypes.PercentBaseContext;
  25. import org.apache.fop.fo.Constants;
  26. import org.apache.fop.fo.GraphicsProperties;
  27. import org.apache.fop.fo.properties.LengthRangeProperty;
  28. /**
  29. * Helper class which calculates the size and position in the viewport of an image.
  30. */
  31. public class ImageLayout implements Constants {
  32. /** logging instance */
  33. protected static final Log log = LogFactory.getLog(ImageLayout.class);
  34. //Input
  35. private GraphicsProperties props;
  36. private PercentBaseContext percentBaseContext;
  37. private Dimension intrinsicSize;
  38. //Output
  39. private Rectangle placement;
  40. private Dimension viewportSize = new Dimension(-1, -1);
  41. private boolean clip;
  42. /**
  43. * Main constructor
  44. * @param props the properties for the image
  45. * @param percentBaseContext the context object for percentage calculations
  46. * @param intrinsicSize the image's intrinsic size
  47. */
  48. public ImageLayout(GraphicsProperties props, PercentBaseContext percentBaseContext,
  49. Dimension intrinsicSize) {
  50. this.props = props;
  51. this.percentBaseContext = percentBaseContext;
  52. this.intrinsicSize = intrinsicSize;
  53. doLayout();
  54. }
  55. /**
  56. * Does the actual calculations for the image.
  57. */
  58. protected void doLayout() {
  59. Length len;
  60. int bpd = -1;
  61. int ipd = -1;
  62. len = props.getBlockProgressionDimension().getOptimum(percentBaseContext).getLength();
  63. if (len.getEnum() != EN_AUTO) {
  64. bpd = evaluateLength(len, intrinsicSize.height);
  65. }
  66. len = props.getBlockProgressionDimension().getMinimum(percentBaseContext).getLength();
  67. if (bpd == -1 && len.getEnum() != EN_AUTO) {
  68. bpd = evaluateLength(len, intrinsicSize.height);
  69. }
  70. len = props.getInlineProgressionDimension().getOptimum(percentBaseContext).getLength();
  71. if (len.getEnum() != EN_AUTO) {
  72. ipd = len.getValue(percentBaseContext);
  73. }
  74. len = props.getInlineProgressionDimension().getMinimum(percentBaseContext).getLength();
  75. if (ipd == -1 && len.getEnum() != EN_AUTO) {
  76. //Establish minimum viewport size
  77. ipd = len.getValue(percentBaseContext);
  78. }
  79. // if auto then use the intrinsic size of the content scaled
  80. // to the content-height and content-width
  81. boolean constrainIntrinsicSize = false;
  82. int cwidth = -1;
  83. int cheight = -1;
  84. len = props.getContentWidth();
  85. if (len.getEnum() != EN_AUTO) {
  86. switch (len.getEnum()) {
  87. case EN_SCALE_TO_FIT:
  88. if (ipd != -1) {
  89. cwidth = ipd;
  90. }
  91. constrainIntrinsicSize = true;
  92. break;
  93. case EN_SCALE_DOWN_TO_FIT:
  94. if (ipd != -1 && intrinsicSize.width > ipd) {
  95. cwidth = ipd;
  96. }
  97. constrainIntrinsicSize = true;
  98. break;
  99. case EN_SCALE_UP_TO_FIT:
  100. if (ipd != -1 && intrinsicSize.width < ipd) {
  101. cwidth = ipd;
  102. }
  103. constrainIntrinsicSize = true;
  104. break;
  105. default:
  106. cwidth = len.getValue(percentBaseContext);
  107. }
  108. }
  109. len = props.getContentHeight();
  110. if (len.getEnum() != EN_AUTO) {
  111. switch (len.getEnum()) {
  112. case EN_SCALE_TO_FIT:
  113. if (bpd != -1) {
  114. cheight = bpd;
  115. }
  116. constrainIntrinsicSize = true;
  117. break;
  118. case EN_SCALE_DOWN_TO_FIT:
  119. if (bpd != -1 && intrinsicSize.height > bpd) {
  120. cheight = bpd;
  121. }
  122. constrainIntrinsicSize = true;
  123. break;
  124. case EN_SCALE_UP_TO_FIT:
  125. if (bpd != -1 && intrinsicSize.height < bpd) {
  126. cheight = bpd;
  127. }
  128. constrainIntrinsicSize = true;
  129. break;
  130. default:
  131. cheight = len.getValue(percentBaseContext);
  132. }
  133. }
  134. Dimension constrainedIntrinsicSize;
  135. if (constrainIntrinsicSize) {
  136. constrainedIntrinsicSize = constrain(intrinsicSize);
  137. } else {
  138. constrainedIntrinsicSize = intrinsicSize;
  139. }
  140. //Derive content extents where not explicit
  141. Dimension adjustedDim = adjustContentSize(cwidth, cheight, constrainedIntrinsicSize);
  142. cwidth = adjustedDim.width;
  143. cheight = adjustedDim.height;
  144. //Adjust viewport if not explicit
  145. if (ipd == -1) {
  146. ipd = constrainExtent(cwidth,
  147. props.getInlineProgressionDimension(), props.getContentWidth());
  148. }
  149. if (bpd == -1) {
  150. bpd = constrainExtent(cheight,
  151. props.getBlockProgressionDimension(), props.getContentHeight());
  152. }
  153. this.clip = false;
  154. int overflow = props.getOverflow();
  155. if (overflow == EN_HIDDEN) {
  156. this.clip = true;
  157. } else if (overflow == EN_ERROR_IF_OVERFLOW) {
  158. if (cwidth > ipd || cheight > bpd) {
  159. //TODO Don't use logging to report error!
  160. log.error("Object overflows the viewport: clipping");
  161. }
  162. this.clip = true;
  163. }
  164. int xoffset = computeXOffset(ipd, cwidth);
  165. int yoffset = computeYOffset(bpd, cheight);
  166. //Build calculation results
  167. this.viewportSize.setSize(ipd, bpd);
  168. this.placement = new Rectangle(xoffset, yoffset, cwidth, cheight);
  169. }
  170. private int constrainExtent(int extent, LengthRangeProperty range, Length contextExtent) {
  171. boolean mayScaleUp = (contextExtent.getEnum() != EN_SCALE_DOWN_TO_FIT);
  172. boolean mayScaleDown = (contextExtent.getEnum() != EN_SCALE_UP_TO_FIT);
  173. Length len;
  174. len = range.getMaximum(percentBaseContext).getLength();
  175. if (len.getEnum() != EN_AUTO) {
  176. int max = evaluateLength(len);
  177. if (max != -1 && mayScaleDown) {
  178. extent = Math.min(extent, max);
  179. }
  180. }
  181. len = range.getMinimum(percentBaseContext).getLength();
  182. if (len.getEnum() != EN_AUTO) {
  183. int min = evaluateLength(len);
  184. if (min != -1 && mayScaleUp) {
  185. extent = Math.max(extent, min);
  186. }
  187. }
  188. return extent;
  189. }
  190. private Dimension constrain(Dimension size) {
  191. Dimension adjusted = new Dimension(size);
  192. int effWidth = constrainExtent(size.width,
  193. props.getInlineProgressionDimension(), props.getContentWidth());
  194. int effHeight = constrainExtent(size.height,
  195. props.getBlockProgressionDimension(), props.getContentHeight());
  196. int scaling = props.getScaling();
  197. if (scaling == EN_UNIFORM) {
  198. double rat1 = (double)effWidth / size.width;
  199. double rat2 = (double)effHeight / size.height;
  200. if (rat1 < rat2) {
  201. adjusted.width = effWidth;
  202. adjusted.height = (int)(rat1 * size.height);
  203. } else if (rat1 > rat2) {
  204. adjusted.width = (int)(rat2 * size.width);
  205. adjusted.height = effHeight;
  206. } else {
  207. adjusted.width = effWidth;
  208. adjusted.height = effHeight;
  209. }
  210. } else {
  211. adjusted.width = effWidth;
  212. adjusted.height = effHeight;
  213. }
  214. return adjusted;
  215. }
  216. private Dimension adjustContentSize(
  217. final int cwidth, final int cheight,
  218. Dimension defaultSize) {
  219. Dimension dim = new Dimension(cwidth, cheight);
  220. int scaling = props.getScaling();
  221. if ((scaling == EN_UNIFORM) || (cwidth == -1) || cheight == -1) {
  222. if (cwidth == -1 && cheight == -1) {
  223. dim.width = defaultSize.width;
  224. dim.height = defaultSize.height;
  225. } else if (cwidth == -1) {
  226. if (defaultSize.height == 0) {
  227. dim.width = 0;
  228. } else {
  229. dim.width = (int)(defaultSize.width * (double)cheight
  230. / defaultSize.height);
  231. }
  232. } else if (cheight == -1) {
  233. if (defaultSize.width == 0) {
  234. dim.height = 0;
  235. } else {
  236. dim.height = (int)(defaultSize.height * (double)cwidth
  237. / defaultSize.width);
  238. }
  239. } else {
  240. // adjust the larger
  241. if (defaultSize.width == 0 || defaultSize.height == 0) {
  242. dim.width = 0;
  243. dim.height = 0;
  244. } else {
  245. double rat1 = (double)cwidth / defaultSize.width;
  246. double rat2 = (double)cheight / defaultSize.height;
  247. if (rat1 < rat2) {
  248. // reduce height
  249. dim.height = (int)(rat1 * defaultSize.height);
  250. } else if (rat1 > rat2) {
  251. dim.width = (int)(rat2 * defaultSize.width);
  252. }
  253. }
  254. }
  255. }
  256. return dim;
  257. }
  258. /**
  259. * Given the ipd and the content width calculates the
  260. * required x offset based on the text-align property
  261. * @param ipd the inline-progression-dimension of the object
  262. * @param cwidth the calculated content width of the object
  263. * @return the X offset
  264. */
  265. public int computeXOffset(int ipd, int cwidth) {
  266. int xoffset = 0;
  267. switch (props.getTextAlign()) {
  268. case EN_CENTER:
  269. xoffset = (ipd - cwidth) / 2;
  270. break;
  271. case EN_END:
  272. xoffset = ipd - cwidth;
  273. break;
  274. case EN_START:
  275. break;
  276. case EN_JUSTIFY:
  277. default:
  278. break;
  279. }
  280. return xoffset;
  281. }
  282. /**
  283. * Given the bpd and the content height calculates the
  284. * required y offset based on the display-align property
  285. * @param bpd the block-progression-dimension of the object
  286. * @param cheight the calculated content height of the object
  287. * @return the Y offset
  288. */
  289. public int computeYOffset(int bpd, int cheight) {
  290. int yoffset = 0;
  291. switch (props.getDisplayAlign()) {
  292. case EN_BEFORE:
  293. break;
  294. case EN_AFTER:
  295. yoffset = bpd - cheight;
  296. break;
  297. case EN_CENTER:
  298. yoffset = (bpd - cheight) / 2;
  299. break;
  300. case EN_AUTO:
  301. default:
  302. break;
  303. }
  304. return yoffset;
  305. }
  306. /**
  307. * Returns the placement of the image inside the viewport.
  308. * @return the placement of the image inside the viewport (coordinates in millipoints)
  309. */
  310. public Rectangle getPlacement() {
  311. return this.placement;
  312. }
  313. /**
  314. * Returns the size of the image's viewport.
  315. * @return the viewport size (in millipoints)
  316. */
  317. public Dimension getViewportSize() {
  318. return this.viewportSize;
  319. }
  320. /**
  321. * Returns the size of the image's intrinsic (natural) size.
  322. * @return the intrinsic size (in millipoints)
  323. */
  324. public Dimension getIntrinsicSize() {
  325. return this.intrinsicSize;
  326. }
  327. /**
  328. * Indicates whether the image is clipped.
  329. * @return true if the image shall be clipped
  330. */
  331. public boolean isClipped() {
  332. return this.clip;
  333. }
  334. private int evaluateLength(Length length, int referenceValue) {
  335. double numericValue = length.getNumericValue(percentBaseContext);
  336. int bpd = numericValue < 0 ? referenceValue : (int) Math.round(numericValue);
  337. return bpd;
  338. }
  339. private int evaluateLength(Length length) {
  340. return evaluateLength(length, -1);
  341. }
  342. }