blob: d062ba2ca4157bf349b133408e42c9d8fa083917 [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.layoutmgr;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.area.Area;
import org.apache.fop.area.Trait;
import org.apache.fop.datatypes.LengthBase;
import org.apache.fop.datatypes.PercentBaseContext;
import org.apache.fop.datatypes.SimplePercentBaseContext;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.CommonMarginBlock;
import org.apache.fop.fo.properties.CommonMarginBlock;
import org.apache.fop.fo.properties.CommonTextDecoration;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
import org.apache.fop.fonts.Font;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.traits.MinOptMax;
/**
* This is a helper class used for setting common traits on areas.
*/
public class TraitSetter {
/** logger */
protected static Log log = LogFactory.getLog(TraitSetter.class);
/**
* Sets border and padding traits on areas.
* @param area area to set the traits on
* @param bpProps border and padding properties
* @param bNotFirst True if the area is not the first area
* @param bNotLast True if the area is not the last area
* @param context Property evaluation context
*/
public static void setBorderPaddingTraits(Area area,
CommonBorderPaddingBackground bpProps, boolean bNotFirst, boolean bNotLast,
PercentBaseContext context) {
int iBP;
iBP = bpProps.getPadding(CommonBorderPaddingBackground.START, bNotFirst, context);
if (iBP > 0) {
area.addTrait(Trait.PADDING_START, new Integer(iBP));
}
iBP = bpProps.getPadding(CommonBorderPaddingBackground.END, bNotLast, context);
if (iBP > 0) {
area.addTrait(Trait.PADDING_END, new Integer(iBP));
}
iBP = bpProps.getPadding(CommonBorderPaddingBackground.BEFORE, false, context);
if (iBP > 0) {
area.addTrait(Trait.PADDING_BEFORE, new Integer(iBP));
}
iBP = bpProps.getPadding(CommonBorderPaddingBackground.AFTER, false, context);
if (iBP > 0) {
area.addTrait(Trait.PADDING_AFTER, new Integer(iBP));
}
addBorderTrait(area, bpProps, bNotFirst,
CommonBorderPaddingBackground.START,
BorderProps.SEPARATE, Trait.BORDER_START);
addBorderTrait(area, bpProps, bNotLast,
CommonBorderPaddingBackground.END,
BorderProps.SEPARATE, Trait.BORDER_END);
addBorderTrait(area, bpProps, false,
CommonBorderPaddingBackground.BEFORE,
BorderProps.SEPARATE, Trait.BORDER_BEFORE);
addBorderTrait(area, bpProps, false,
CommonBorderPaddingBackground.AFTER,
BorderProps.SEPARATE, Trait.BORDER_AFTER);
}
/**
* Sets border traits on an area.
* @param area area to set the traits on
* @param bpProps border and padding properties
* @param mode the border paint mode (see BorderProps)
*/
private static void addBorderTrait(Area area,
CommonBorderPaddingBackground bpProps,
boolean bDiscard, int iSide, int mode,
Object oTrait) {
int iBP = bpProps.getBorderWidth(iSide, bDiscard);
if (iBP > 0) {
area.addTrait(oTrait,
new BorderProps(bpProps.getBorderStyle(iSide),
iBP, bpProps.getBorderColor(iSide),
mode));
}
}
/**
* Add borders to an area. Note: this method also adds unconditional padding. Don't use!
* Layout managers that create areas with borders can use this to
* add the borders to the area.
* @param area the area to set the traits on.
* @param bordProps border properties
* @param context Property evaluation context
* @deprecated Call the other addBorders() method and addPadding separately.
*/
public static void addBorders(Area area, CommonBorderPaddingBackground bordProps,
PercentBaseContext context) {
BorderProps bps = getBorderProps(bordProps, CommonBorderPaddingBackground.BEFORE);
if (bps != null) {
area.addTrait(Trait.BORDER_BEFORE, bps);
}
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.AFTER);
if (bps != null) {
area.addTrait(Trait.BORDER_AFTER, bps);
}
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.START);
if (bps != null) {
area.addTrait(Trait.BORDER_START, bps);
}
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.END);
if (bps != null) {
area.addTrait(Trait.BORDER_END, bps);
}
addPadding(area, bordProps, context);
}
/**
* Add borders to an area.
* Layout managers that create areas with borders can use this to
* add the borders to the area.
* @param area the area to set the traits on.
* @param bordProps border properties
* @param discardBefore true if the before border should be discarded
* @param discardAfter true if the after border should be discarded
* @param discardStart true if the start border should be discarded
* @param discardEnd true if the end border should be discarded
* @param context Property evaluation context
*/
public static void addBorders(Area area, CommonBorderPaddingBackground bordProps,
boolean discardBefore, boolean discardAfter,
boolean discardStart, boolean discardEnd,
PercentBaseContext context) {
BorderProps bps = getBorderProps(bordProps, CommonBorderPaddingBackground.BEFORE);
if (bps != null && !discardBefore) {
area.addTrait(Trait.BORDER_BEFORE, bps);
}
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.AFTER);
if (bps != null && !discardAfter) {
area.addTrait(Trait.BORDER_AFTER, bps);
}
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.START);
if (bps != null && !discardStart) {
area.addTrait(Trait.BORDER_START, bps);
}
bps = getBorderProps(bordProps, CommonBorderPaddingBackground.END);
if (bps != null && !discardEnd) {
area.addTrait(Trait.BORDER_END, bps);
}
}
/**
* Add borders to an area for the collapsing border model in tables.
* Layout managers that create areas with borders can use this to
* add the borders to the area.
* @param area the area to set the traits on.
* @param borderBefore the resolved before border
* @param borderAfter the resolved after border
* @param borderStart the resolved start border
* @param borderEnd the resolved end border
* @param outer 4 boolean values indicating if the side represents the
* table's outer border. Order: before, after, start, end
*/
public static void addCollapsingBorders(Area area,
BorderInfo borderBefore, BorderInfo borderAfter,
BorderInfo borderStart, BorderInfo borderEnd,
boolean[] outer) {
BorderProps bps = getCollapsingBorderProps(borderBefore, outer[0]);
if (bps != null) {
area.addTrait(Trait.BORDER_BEFORE, bps);
}
bps = getCollapsingBorderProps(borderAfter, outer[1]);
if (bps != null) {
area.addTrait(Trait.BORDER_AFTER, bps);
}
bps = getCollapsingBorderProps(borderStart, outer[2]);
if (bps != null) {
area.addTrait(Trait.BORDER_START, bps);
}
bps = getCollapsingBorderProps(borderEnd, outer[3]);
if (bps != null) {
area.addTrait(Trait.BORDER_END, bps);
}
}
private static void addPadding(Area area, CommonBorderPaddingBackground bordProps,
PercentBaseContext context) {
addPadding(area, bordProps, false, false, false, false, context);
}
/**
* Add padding to an area.
* Layout managers that create areas with padding can use this to
* add the borders to the area.
* @param area the area to set the traits on.
* @param bordProps border and padding properties
* @param discardBefore true if the before padding should be discarded
* @param discardAfter true if the after padding should be discarded
* @param discardStart true if the start padding should be discarded
* @param discardEnd true if the end padding should be discarded
* @param context Property evaluation context
*/
public static void addPadding(Area area, CommonBorderPaddingBackground bordProps,
boolean discardBefore, boolean discardAfter,
boolean discardStart, boolean discardEnd,
PercentBaseContext context) {
int padding = bordProps.getPadding(CommonBorderPaddingBackground.BEFORE,
discardBefore, context);
if (padding != 0) {
area.addTrait(Trait.PADDING_BEFORE, new java.lang.Integer(padding));
}
padding = bordProps.getPadding(CommonBorderPaddingBackground.AFTER,
discardAfter, context);
if (padding != 0) {
area.addTrait(Trait.PADDING_AFTER, new java.lang.Integer(padding));
}
padding = bordProps.getPadding(CommonBorderPaddingBackground.START,
discardStart, context);
if (padding != 0) {
area.addTrait(Trait.PADDING_START, new java.lang.Integer(padding));
}
padding = bordProps.getPadding(CommonBorderPaddingBackground.END,
discardEnd, context);
if (padding != 0) {
area.addTrait(Trait.PADDING_END, new java.lang.Integer(padding));
}
}
private static BorderProps getBorderProps(CommonBorderPaddingBackground bordProps, int side) {
int width = bordProps.getBorderWidth(side, false);
if (width != 0) {
BorderProps bps;
bps = new BorderProps(bordProps.getBorderStyle(side),
width,
bordProps.getBorderColor(side),
BorderProps.SEPARATE);
return bps;
} else {
return null;
}
}
private static BorderProps getCollapsingBorderProps(BorderInfo borderInfo, boolean outer) {
assert borderInfo != null;
int width = borderInfo.getRetainedWidth();
if (width != 0) {
BorderProps bps = new BorderProps(borderInfo.getStyle(), width, borderInfo.getColor(),
(outer ? BorderProps.COLLAPSE_OUTER : BorderProps.COLLAPSE_INNER));
return bps;
} else {
return null;
}
}
/**
* Add background to an area. This method is mainly used by table-related layout
* managers to add background for column, body or row. Since the area corresponding to
* border-separation must be filled with the table's background, for every cell an
* additional area with the same dimensions is created to hold the background for the
* corresponding column/body/row. An additional shift must then be added to
* background-position-horizontal/vertical to ensure the background images are
* correctly placed. Indeed the placement of images must be made WRT the
* column/body/row and not the cell.
*
* <p>Note: The area's IPD and BPD must be set before calling this method.</p>
*
* <p>TODO the regular
* {@link #addBackground(Area, CommonBorderPaddingBackground, PercentBaseContext)}
* method should be used instead, and a means to retrieve the original area's
* dimensions must be found.</p>
*
* <p>TODO the placement of images in the x- or y-direction will be incorrect if
* background-repeat is set for that direction.</p>
*
* @param area the area to set the traits on
* @param backProps the background properties
* @param context Property evaluation context
* @param ipdShift horizontal shift to affect to the background, in addition to the
* value of the background-position-horizontal property
* @param bpdShift vertical shift to affect to the background, in addition to the
* value of the background-position-vertical property
* @param referenceIPD value to use as a reference for percentage calculation
* @param referenceBPD value to use as a reference for percentage calculation
*/
public static void addBackground(Area area,
CommonBorderPaddingBackground backProps,
PercentBaseContext context,
int ipdShift, int bpdShift, int referenceIPD, int referenceBPD) {
if (!backProps.hasBackground()) {
return;
}
Trait.Background back = new Trait.Background();
back.setColor(backProps.backgroundColor);
if (backProps.getImageInfo() != null) {
back.setURL(backProps.backgroundImage);
back.setImageInfo(backProps.getImageInfo());
back.setRepeat(backProps.backgroundRepeat);
if (backProps.backgroundPositionHorizontal != null) {
if (back.getRepeat() == Constants.EN_NOREPEAT
|| back.getRepeat() == Constants.EN_REPEATY) {
if (area.getIPD() > 0) {
PercentBaseContext refContext = new SimplePercentBaseContext(context,
LengthBase.IMAGE_BACKGROUND_POSITION_HORIZONTAL,
(referenceIPD - back.getImageInfo().getSize().getWidthMpt()));
back.setHoriz(ipdShift
+ backProps.backgroundPositionHorizontal.getValue(refContext));
} else {
// TODO Area IPD has to be set for this to work
log.warn("Horizontal background image positioning ignored"
+ " because the IPD was not set on the area."
+ " (Yes, it's a bug in FOP)");
}
}
}
if (backProps.backgroundPositionVertical != null) {
if (back.getRepeat() == Constants.EN_NOREPEAT
|| back.getRepeat() == Constants.EN_REPEATX) {
if (area.getBPD() > 0) {
PercentBaseContext refContext = new SimplePercentBaseContext(context,
LengthBase.IMAGE_BACKGROUND_POSITION_VERTICAL,
(referenceBPD - back.getImageInfo().getSize().getHeightMpt()));
back.setVertical(bpdShift
+ backProps.backgroundPositionVertical.getValue(refContext));
} else {
// TODO Area BPD has to be set for this to work
log.warn("Vertical background image positioning ignored"
+ " because the BPD was not set on the area."
+ " (Yes, it's a bug in FOP)");
}
}
}
}
area.addTrait(Trait.BACKGROUND, back);
}
/**
* Add background to an area.
* Layout managers that create areas with a background can use this to
* add the background to the area.
* Note: The area's IPD and BPD must be set before calling this method.
* @param area the area to set the traits on
* @param backProps the background properties
* @param context Property evaluation context
*/
public static void addBackground(Area area,
CommonBorderPaddingBackground backProps,
PercentBaseContext context) {
if (!backProps.hasBackground()) {
return;
}
Trait.Background back = new Trait.Background();
back.setColor(backProps.backgroundColor);
if (backProps.getImageInfo() != null) {
back.setURL(backProps.backgroundImage);
back.setImageInfo(backProps.getImageInfo());
back.setRepeat(backProps.backgroundRepeat);
if (backProps.backgroundPositionHorizontal != null) {
if (back.getRepeat() == Constants.EN_NOREPEAT
|| back.getRepeat() == Constants.EN_REPEATY) {
if (area.getIPD() > 0) {
int width = area.getIPD();
width += backProps.getPaddingStart(false, context);
width += backProps.getPaddingEnd(false, context);
int imageWidthMpt = back.getImageInfo().getSize().getWidthMpt();
int lengthBaseValue = width - imageWidthMpt;
SimplePercentBaseContext simplePercentBaseContext
= new SimplePercentBaseContext(context,
LengthBase.IMAGE_BACKGROUND_POSITION_HORIZONTAL,
lengthBaseValue);
int horizontal = backProps.backgroundPositionHorizontal.getValue(
simplePercentBaseContext);
back.setHoriz(horizontal);
} else {
//TODO Area IPD has to be set for this to work
log.warn("Horizontal background image positioning ignored"
+ " because the IPD was not set on the area."
+ " (Yes, it's a bug in FOP)");
}
}
}
if (backProps.backgroundPositionVertical != null) {
if (back.getRepeat() == Constants.EN_NOREPEAT
|| back.getRepeat() == Constants.EN_REPEATX) {
if (area.getBPD() > 0) {
int height = area.getBPD();
height += backProps.getPaddingBefore(false, context);
height += backProps.getPaddingAfter(false, context);
int imageHeightMpt = back.getImageInfo().getSize().getHeightMpt();
int lengthBaseValue = height - imageHeightMpt;
SimplePercentBaseContext simplePercentBaseContext
= new SimplePercentBaseContext(context,
LengthBase.IMAGE_BACKGROUND_POSITION_VERTICAL,
lengthBaseValue);
int vertical = backProps.backgroundPositionVertical.getValue(
simplePercentBaseContext);
back.setVertical(vertical);
} else {
//TODO Area BPD has to be set for this to work
log.warn("Vertical background image positioning ignored"
+ " because the BPD was not set on the area."
+ " (Yes, it's a bug in FOP)");
}
}
}
}
area.addTrait(Trait.BACKGROUND, back);
}
/**
* Add space to a block area.
* Layout managers that create block areas can use this to add space
* outside of the border rectangle to the area.
* @param area the area to set the traits on.
* @param bpProps the border, padding and background properties
* @param startIndent the effective start-indent value
* @param endIndent the effective end-indent value
* @param context the context for evaluation of percentages
*/
public static void addMargins(Area area,
CommonBorderPaddingBackground bpProps,
int startIndent, int endIndent,
PercentBaseContext context) {
if (startIndent != 0) {
area.addTrait(Trait.START_INDENT, new Integer(startIndent));
}
int spaceStart = startIndent
- bpProps.getBorderStartWidth(false)
- bpProps.getPaddingStart(false, context);
if (spaceStart != 0) {
area.addTrait(Trait.SPACE_START, new Integer(spaceStart));
}
if (endIndent != 0) {
area.addTrait(Trait.END_INDENT, new Integer(endIndent));
}
int spaceEnd = endIndent
- bpProps.getBorderEndWidth(false)
- bpProps.getPaddingEnd(false, context);
if (spaceEnd != 0) {
area.addTrait(Trait.SPACE_END, new Integer(spaceEnd));
}
}
/**
* Add space to a block area.
* Layout managers that create block areas can use this to add space
* outside of the border rectangle to the area.
* @param area the area to set the traits on.
* @param bpProps the border, padding and background properties
* @param marginProps the margin properties.
* @param context the context for evaluation of percentages
*/
public static void addMargins(Area area,
CommonBorderPaddingBackground bpProps,
CommonMarginBlock marginProps,
PercentBaseContext context) {
int startIndent = marginProps.startIndent.getValue(context);
int endIndent = marginProps.endIndent.getValue(context);
addMargins(area, bpProps, startIndent, endIndent, context);
}
/**
* Returns the effective space length of a resolved space specifier based on the adjustment
* value.
* @param adjust the adjustment value
* @param space the space specifier
* @return the effective space length
*/
public static int getEffectiveSpace(double adjust, MinOptMax space) {
if (space == null) {
return 0;
}
int sp = space.opt;
if (adjust > 0) {
sp = sp + (int)(adjust * (space.max - space.opt));
} else {
sp = sp + (int)(adjust * (space.opt - space.min));
}
return sp;
}
/**
* Adds traits for space-before and space-after to an area.
* @param area the target area
* @param adjust the adjustment value
* @param spaceBefore the space-before space specifier
* @param spaceAfter the space-after space specifier
*/
public static void addSpaceBeforeAfter(Area area, double adjust,
MinOptMax spaceBefore, MinOptMax spaceAfter) {
int space;
space = getEffectiveSpace(adjust, spaceBefore);
if (space != 0) {
area.addTrait(Trait.SPACE_BEFORE, new Integer(space));
}
space = getEffectiveSpace(adjust, spaceAfter);
if (space != 0) {
area.addTrait(Trait.SPACE_AFTER, new Integer(space));
}
}
/**
* Sets the traits for breaks on an area.
* @param area the area to set the traits on.
* @param breakBefore the value for break-before
* @param breakAfter the value for break-after
*/
public static void addBreaks(Area area, int breakBefore, int breakAfter) {
/* Currently disabled as these traits are never used by the renderers
area.addTrait(Trait.BREAK_AFTER, new Integer(breakAfter));
area.addTrait(Trait.BREAK_BEFORE, new Integer(breakBefore));
*/
}
/**
* Adds font traits to an area
* @param area the target are
* @param font the font to use
*/
public static void addFontTraits(Area area, Font font) {
area.addTrait(Trait.FONT, font.getFontTriplet());
area.addTrait(Trait.FONT_SIZE, new Integer(font.getFontSize()));
}
/**
* Adds the text-decoration traits to the area.
* @param area the area to set the traits on
* @param deco the text decorations
*/
public static void addTextDecoration(Area area, CommonTextDecoration deco) {
//TODO Finish text-decoration
if (deco != null) {
if (deco.hasUnderline()) {
area.addTrait(Trait.UNDERLINE, Boolean.TRUE);
area.addTrait(Trait.UNDERLINE_COLOR, deco.getUnderlineColor());
}
if (deco.hasOverline()) {
area.addTrait(Trait.OVERLINE, Boolean.TRUE);
area.addTrait(Trait.OVERLINE_COLOR, deco.getOverlineColor());
}
if (deco.hasLineThrough()) {
area.addTrait(Trait.LINETHROUGH, Boolean.TRUE);
area.addTrait(Trait.LINETHROUGH_COLOR, deco.getLineThroughColor());
}
if (deco.isBlinking()) {
area.addTrait(Trait.BLINK, Boolean.TRUE);
}
}
}
/**
* Adds the ptr trait to the area.
* @param area the area to set the traits on
* @param ptr string
*/
public static void addPtr(Area area, String ptr) {
if (ptr != null && ptr.length() > 0) {
area.addTrait(Trait.PTR, ptr);
}
}
/**
* Sets the producer's ID as a trait on the area. This can be used to track back the
* generating FO node.
* @param area the area to set the traits on
* @param id the ID to set
*/
public static void setProducerID(Area area, String id) {
if (id != null && id.length() > 0) {
area.addTrait(Trait.PROD_ID, id);
}
}
}