123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- /*
- * 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.layoutmgr.inline;
-
- import java.awt.Dimension;
- import java.awt.Rectangle;
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- import org.apache.fop.datatypes.Length;
- import org.apache.fop.datatypes.PercentBaseContext;
- import org.apache.fop.fo.Constants;
- import org.apache.fop.fo.GraphicsProperties;
- import org.apache.fop.fo.properties.LengthRangeProperty;
-
- /**
- * Helper class which calculates the size and position in the viewport of an image.
- */
- public class ImageLayout implements Constants {
-
- /** logging instance */
- protected static final Log log = LogFactory.getLog(ImageLayout.class);
-
- //Input
- private GraphicsProperties props;
- private PercentBaseContext percentBaseContext;
- private Dimension intrinsicSize;
-
- //Output
- private Rectangle placement;
- private Dimension viewportSize = new Dimension(-1, -1);
- private boolean clip;
-
- /**
- * Main constructor
- * @param props the properties for the image
- * @param percentBaseContext the context object for percentage calculations
- * @param intrinsicSize the image's intrinsic size
- */
- public ImageLayout(GraphicsProperties props, PercentBaseContext percentBaseContext,
- Dimension intrinsicSize) {
- this.props = props;
- this.percentBaseContext = percentBaseContext;
- this.intrinsicSize = intrinsicSize;
-
- doLayout();
- }
-
- /**
- * Does the actual calculations for the image.
- */
- protected void doLayout() {
- Length len;
-
- int bpd = -1;
- int ipd = -1;
-
- len = props.getBlockProgressionDimension().getOptimum(percentBaseContext).getLength();
- if (len.getEnum() != EN_AUTO) {
- bpd = len.getValue(percentBaseContext);
- }
- len = props.getBlockProgressionDimension().getMinimum(percentBaseContext).getLength();
- if (bpd == -1 && len.getEnum() != EN_AUTO) {
- //Establish minimum viewport size
- bpd = len.getValue(percentBaseContext);
- }
-
- len = props.getInlineProgressionDimension().getOptimum(percentBaseContext).getLength();
- if (len.getEnum() != EN_AUTO) {
- ipd = len.getValue(percentBaseContext);
- }
- len = props.getInlineProgressionDimension().getMinimum(percentBaseContext).getLength();
- if (ipd == -1 && len.getEnum() != EN_AUTO) {
- //Establish minimum viewport size
- ipd = len.getValue(percentBaseContext);
- }
-
- // if auto then use the intrinsic size of the content scaled
- // to the content-height and content-width
- boolean constrainIntrinsicSize = false;
- int cwidth = -1;
- int cheight = -1;
- len = props.getContentWidth();
- if (len.getEnum() != EN_AUTO) {
- switch (len.getEnum()) {
- case EN_SCALE_TO_FIT:
- if (ipd != -1) {
- cwidth = ipd;
- }
- constrainIntrinsicSize = true;
- break;
- case EN_SCALE_DOWN_TO_FIT:
- if (ipd != -1 && intrinsicSize.width > ipd) {
- cwidth = ipd;
- }
- constrainIntrinsicSize = true;
- break;
- case EN_SCALE_UP_TO_FIT:
- if (ipd != -1 && intrinsicSize.width < ipd) {
- cwidth = ipd;
- }
- constrainIntrinsicSize = true;
- break;
- default:
- cwidth = len.getValue(percentBaseContext);
- }
- }
- len = props.getContentHeight();
- if (len.getEnum() != EN_AUTO) {
- switch (len.getEnum()) {
- case EN_SCALE_TO_FIT:
- if (bpd != -1) {
- cheight = bpd;
- }
- constrainIntrinsicSize = true;
- break;
- case EN_SCALE_DOWN_TO_FIT:
- if (bpd != -1 && intrinsicSize.height > bpd) {
- cheight = bpd;
- }
- constrainIntrinsicSize = true;
- break;
- case EN_SCALE_UP_TO_FIT:
- if (bpd != -1 && intrinsicSize.height < bpd) {
- cheight = bpd;
- }
- constrainIntrinsicSize = true;
- break;
- default:
- cheight = len.getValue(percentBaseContext);
- }
- }
-
- Dimension constrainedIntrinsicSize;
- if (constrainIntrinsicSize) {
- constrainedIntrinsicSize = constrain(intrinsicSize);
- } else {
- constrainedIntrinsicSize = intrinsicSize;
- }
-
- //Derive content extents where not explicit
- Dimension adjustedDim = adjustContentSize(cwidth, cheight, constrainedIntrinsicSize);
- cwidth = adjustedDim.width;
- cheight = adjustedDim.height;
-
- //Adjust viewport if not explicit
- if (ipd == -1) {
- ipd = constrainExtent(cwidth,
- props.getInlineProgressionDimension(), props.getContentWidth());
- }
- if (bpd == -1) {
- bpd = constrainExtent(cheight,
- props.getBlockProgressionDimension(), props.getContentHeight());
- }
-
- this.clip = false;
- int overflow = props.getOverflow();
- if (overflow == EN_HIDDEN) {
- this.clip = true;
- } else if (overflow == EN_ERROR_IF_OVERFLOW) {
- if (cwidth > ipd || cheight > bpd) {
- //TODO Don't use logging to report error!
- log.error("Object overflows the viewport: clipping");
- }
- this.clip = true;
- }
-
- int xoffset = computeXOffset(ipd, cwidth);
- int yoffset = computeYOffset(bpd, cheight);
-
- //Build calculation results
- this.viewportSize.setSize(ipd, bpd);
- this.placement = new Rectangle(xoffset, yoffset, cwidth, cheight);
- }
-
- private int constrainExtent(int extent, LengthRangeProperty range, Length contextExtent) {
- boolean mayScaleUp = (contextExtent.getEnum() != EN_SCALE_DOWN_TO_FIT);
- boolean mayScaleDown = (contextExtent.getEnum() != EN_SCALE_UP_TO_FIT);
- Length len;
- len = range.getMaximum(percentBaseContext).getLength();
- if (len.getEnum() != EN_AUTO) {
- int max = len.getValue(percentBaseContext);
- if (max != -1 && mayScaleDown) {
- extent = Math.min(extent, max);
- }
- }
- len = range.getMinimum(percentBaseContext).getLength();
- if (len.getEnum() != EN_AUTO) {
- int min = len.getValue(percentBaseContext);
- if (min != -1 && mayScaleUp) {
- extent = Math.max(extent, min);
- }
- }
- return extent;
- }
-
- private Dimension constrain(Dimension size) {
- Dimension adjusted = new Dimension(size);
- int effWidth = constrainExtent(size.width,
- props.getInlineProgressionDimension(), props.getContentWidth());
- int effHeight = constrainExtent(size.height,
- props.getBlockProgressionDimension(), props.getContentHeight());
- int scaling = props.getScaling();
- if (scaling == EN_UNIFORM) {
- double rat1 = (double)effWidth / size.width;
- double rat2 = (double)effHeight / size.height;
- if (rat1 < rat2) {
- adjusted.width = effWidth;
- adjusted.height = (int)(rat1 * size.height);
- } else if (rat1 > rat2) {
- adjusted.width = (int)(rat2 * size.width);
- adjusted.height = effHeight;
- }
- } else {
- adjusted.width = effWidth;
- adjusted.height = effHeight;
- }
- return adjusted;
- }
-
- private Dimension adjustContentSize(
- final int cwidth, final int cheight,
- Dimension defaultSize) {
- Dimension dim = new Dimension(cwidth, cheight);
- int scaling = props.getScaling();
- if ((scaling == EN_UNIFORM) || (cwidth == -1) || cheight == -1) {
- if (cwidth == -1 && cheight == -1) {
- dim.width = defaultSize.width;
- dim.height = defaultSize.height;
- } else if (cwidth == -1) {
- if (defaultSize.height == 0) {
- dim.width = 0;
- } else {
- dim.width = (int)(defaultSize.width * (double)cheight
- / defaultSize.height);
- }
- } else if (cheight == -1) {
- if (defaultSize.width == 0) {
- dim.height = 0;
- } else {
- dim.height = (int)(defaultSize.height * (double)cwidth
- / defaultSize.width);
- }
- } else {
- // adjust the larger
- if (defaultSize.width == 0 || defaultSize.height == 0) {
- dim.width = 0;
- dim.height = 0;
- } else {
- double rat1 = (double)cwidth / defaultSize.width;
- double rat2 = (double)cheight / defaultSize.height;
- if (rat1 < rat2) {
- // reduce height
- dim.height = (int)(rat1 * defaultSize.height);
- } else if (rat1 > rat2) {
- dim.width = (int)(rat2 * defaultSize.width);
- }
- }
- }
- }
- return dim;
- }
-
- /**
- * Given the ipd and the content width calculates the
- * required x offset based on the text-align property
- * @param ipd the inline-progression-dimension of the object
- * @param cwidth the calculated content width of the object
- * @return the X offset
- */
- public int computeXOffset (int ipd, int cwidth) {
- int xoffset = 0;
- switch (props.getTextAlign()) {
- case EN_CENTER:
- xoffset = (ipd - cwidth) / 2;
- break;
- case EN_END:
- xoffset = ipd - cwidth;
- break;
- case EN_START:
- break;
- case EN_JUSTIFY:
- default:
- break;
- }
- return xoffset;
- }
-
- /**
- * Given the bpd and the content height calculates the
- * required y offset based on the display-align property
- * @param bpd the block-progression-dimension of the object
- * @param cheight the calculated content height of the object
- * @return the Y offset
- */
- public int computeYOffset(int bpd, int cheight) {
- int yoffset = 0;
- switch (props.getDisplayAlign()) {
- case EN_BEFORE:
- break;
- case EN_AFTER:
- yoffset = bpd - cheight;
- break;
- case EN_CENTER:
- yoffset = (bpd - cheight) / 2;
- break;
- case EN_AUTO:
- default:
- break;
- }
- return yoffset;
- }
-
- /**
- * Returns the placement of the image inside the viewport.
- * @return the placement of the image inside the viewport (coordinates in millipoints)
- */
- public Rectangle getPlacement() {
- return this.placement;
- }
-
- /**
- * Returns the size of the image's viewport.
- * @return the viewport size (in millipoints)
- */
- public Dimension getViewportSize() {
- return this.viewportSize;
- }
-
- /**
- * Returns the size of the image's intrinsic (natural) size.
- * @return the intrinsic size (in millipoints)
- */
- public Dimension getIntrinsicSize() {
- return this.intrinsicSize;
- }
-
- /**
- * Indicates whether the image is clipped.
- * @return true if the image shall be clipped
- */
- public boolean isClipped() {
- return this.clip;
- }
-
- }
|