/* * 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.area; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.apache.fop.area.inline.InlineArea; import static org.apache.fop.fo.Constants.EN_CENTER; import static org.apache.fop.fo.Constants.EN_END; import static org.apache.fop.fo.Constants.EN_JUSTIFY; import static org.apache.fop.fo.Constants.EN_START; /** * The line area. * This is a line area that contains inline areas. */ public class LineArea extends Area { private static final long serialVersionUID = 7670235908329290684L; /** * this class stores information about line width and potential adjustments * that can be used in order to re-compute adjustement and / or indents when a * page-number or a page-number-citation is resolved */ // @SuppressFBWarnings("SE_INNER_CLASS") private final class LineAdjustingInfo implements Serializable { private static final long serialVersionUID = -6103629976229458273L; private int lineAlignment; private int difference; private int availableStretch; private int availableShrink; private double variationFactor; private boolean bAddedToAreaTree; private LineAdjustingInfo(int alignment, int diff, int stretch, int shrink) { lineAlignment = alignment; difference = diff; availableStretch = stretch; availableShrink = shrink; variationFactor = 1.0; bAddedToAreaTree = false; } /** {@inheritDoc} */ public String toString() { return getClass().getSimpleName() + ": diff=" + difference + ", variation=" + variationFactor + ", stretch=" + availableStretch + ", shrink=" + availableShrink; } } private LineAdjustingInfo adjustingInfo; // this class can contain the dominant char styling info // this means that many renderers can optimise a bit private List<InlineArea> inlineAreas = new ArrayList<InlineArea>(); /** * default constructor: * nothing to do */ public LineArea() { } /** * constructor with extra parameters: * a new LineAdjustingInfo object is created * @param alignment alignment of this line * @param diff difference between content width and line width * @param stretch the available stretch for any adjustments * @param shrink the available shrink for any adjustments */ public LineArea(int alignment, int diff, int stretch, int shrink) { adjustingInfo = new LineAdjustingInfo(alignment, diff, stretch, shrink); } /** * Add a child area to this line area. * * @param childArea the inline child area to add */ @Override public void addChildArea(Area childArea) { if (childArea instanceof InlineArea) { addInlineArea((InlineArea)childArea); // set the parent area for the child area ((InlineArea)childArea).setParentArea(this); } } /** * Add an inline child area to this line area. * * @param area the inline child area to add */ public void addInlineArea(InlineArea area) { inlineAreas.add(area); } /** * <p>Set (en masse) the inline child areas of this line area.</p> * <p> Used by bidirectional processing after line area consituent reordering.</p> * @param inlineAreas the list of inline areas */ public void setInlineAreas(List inlineAreas) { for (InlineArea ia : (Iterable<InlineArea>) inlineAreas) { Area pa = ia.getParentArea(); if (pa == null) { ia.setParentArea(this); } else { assert pa == this; } } this.inlineAreas = inlineAreas; } /** * Get the inline child areas of this line area. * * @return the list of inline areas */ public List getInlineAreas() { return inlineAreas; } /** * Get the start indent of this line area. * The start indent is used for offsetting the start of * the inline areas for alignment or other indents. * * @return the start indent value */ public int getStartIndent() { if (hasTrait(Trait.START_INDENT)) { return getTraitAsInteger(Trait.START_INDENT); } else { return 0; } } /** * Get the end indent of this line area. * The end indent is used for offsetting the end of * the inline areas for alignment or other indents. * * @return the end indent value */ public int getEndIndent() { if (hasTrait(Trait.END_INDENT)) { return getTraitAsInteger(Trait.END_INDENT); } else { return 0; } } /** * Updates the extents of the line area from its children. */ public void updateExtentsFromChildren() { int ipd = 0; int bpd = 0; for (InlineArea inlineArea : inlineAreas) { ipd = Math.max(ipd, inlineArea.getAllocIPD()); bpd += inlineArea.getAllocBPD(); } setIPD(ipd); setBPD(bpd); } /** * receive notification about the ipd variation of a descendant area * and perform the needed adjustment, according to the alignment; * in particular: * <ul> * <li>left-aligned text needs no adjustement;</li> * <li>right-aligned text and centered text are handled locally, * adjusting the indent of this LineArea;</li> * <li>justified text requires a more complex adjustment, as the * variation factor computed on the basis of the total * stretch and shrink of the line must be applied in every * descendant leaf areas (text areas and leader areas).</li> * </ul> * @param ipdVariation the difference between old and new ipd */ public void handleIPDVariation(int ipdVariation) { int si = getStartIndent(); int ei = getEndIndent(); switch (adjustingInfo.lineAlignment) { case EN_START: // adjust end indent addTrait(Trait.END_INDENT, ei - ipdVariation); break; case EN_CENTER: // adjust start and end indents addTrait(Trait.START_INDENT, si - ipdVariation / 2); addTrait(Trait.END_INDENT, ei - ipdVariation / 2); break; case EN_END: // adjust start indent addTrait(Trait.START_INDENT, si - ipdVariation); break; case EN_JUSTIFY: // compute variation factor adjustingInfo.variationFactor *= (float) (adjustingInfo.difference - ipdVariation) / adjustingInfo.difference; adjustingInfo.difference -= ipdVariation; // if the LineArea has already been added to the area tree, // call finalize(); otherwise, wait for the LineLM to call it if (adjustingInfo.bAddedToAreaTree) { finish(); } break; default: throw new RuntimeException(); } } /** * apply the variation factor to all descendant areas * and destroy the AdjustingInfo object if there are * no UnresolvedAreas left */ public void finish() { if (adjustingInfo.lineAlignment == EN_JUSTIFY) { if (log.isTraceEnabled()) { log.trace("Applying variation factor to justified line: " + adjustingInfo); } // justified line: apply the variation factor boolean bUnresolvedAreasPresent = false; // recursively apply variation factor to descendant areas for (InlineArea inlineArea : inlineAreas) { bUnresolvedAreasPresent |= inlineArea .applyVariationFactor(adjustingInfo.variationFactor, adjustingInfo.availableStretch, adjustingInfo.availableShrink); } if (!bUnresolvedAreasPresent) { // there are no more UnresolvedAreas: // destroy the AdjustingInfo instance adjustingInfo = null; } else { // this method will be called again later: // the first time, it is called by the LineLM, // afterwards it must be called by the LineArea itself if (!adjustingInfo.bAddedToAreaTree) { adjustingInfo.bAddedToAreaTree = true; } // reset the variation factor adjustingInfo.variationFactor = 1.0; } } else { // the line is not justified: the ipd variation has already // been handled, modifying the line indent } } public int getEffectiveIPD() { int maxIPD = 0; if (inlineAreas != null) { for (Area area : inlineAreas) { int effectiveIPD = area.getEffectiveIPD(); if (effectiveIPD > maxIPD) { maxIPD = effectiveIPD; } } } return maxIPD; } }