/* * 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 java.util.Collections; import java.util.List; import org.apache.fop.fo.Constants; import org.apache.fop.layoutmgr.inline.AlignmentContext; import org.apache.fop.layoutmgr.inline.HyphContext; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.WritingMode; /** * This class is used to pass information to the getNextKnuthElements() * method. It is set up by higher level LM and used by lower level LM. */ public final class LayoutContext { /** Generated break possibility is first in a new area */ public static final int NEW_AREA = 0x01; /** * If this flag is set, it indicates that any break-before values other than "auto" should * not cause a mandatory break as this break was already handled by a parent layout manager. */ public static final int SUPPRESS_BREAK_BEFORE = 0x02; public static final int FIRST_AREA = 0x04; public static final int LAST_AREA = 0x08; public static final int RESOLVE_LEADING_SPACE = 0x10; private static final int TREAT_AS_ARTIFACT = 0x20; private int flags; // Contains some set of flags defined above /** * Total available stacking dimension for a "galley-level" layout * manager in block-progression-direction. It is passed by the * parent LM. * These LM <b>may</b> wish to pass this information down to lower * level LM to allow them to optimize returned break possibilities. */ private MinOptMax stackLimitBP; /** to keep track of spanning in multi-column layout */ private int currentSpan = Constants.NOT_SET; private int nextSpan = Constants.NOT_SET; /** inline-progression-dimension of nearest ancestor reference area */ private int refIPD; //TODO After the split of stackLimit into stackLimitBP and stackLimitIP there's now some //overlap with refIPD. Need to investigate how best to refactor that. /** the writing mode established by the nearest ancestor reference area */ private WritingMode writingMode = WritingMode.LR_TB; /** Current pending space-after or space-end from preceding area */ private SpaceSpecifier trailingSpace; /** Current pending space-before or space-start from ancestor areas */ private SpaceSpecifier leadingSpace; /** * A list of pending marks (border and padding) on the after edge when a page break occurs. * May be null. */ private List pendingAfterMarks; /** * A list of pending marks (border and padding) on the before edge when a page break occurs. * May be null. */ private List pendingBeforeMarks; /** Current hyphenation context. May be null. */ private HyphContext hyphContext; /** Alignment in BP direction */ private int bpAlignment = Constants.EN_START; /** Stretch or shrink value when making areas. */ private double ipdAdjust; /** Stretch or shrink value when adding spaces. */ private double dSpaceAdjust; private AlignmentContext alignmentContext; /** Amount of space before / start */ private int spaceBefore; /** Amount of space after / end */ private int spaceAfter; /** Amount of space to reserve at the beginning of each line */ private int lineStartBorderAndPaddingWidth; /** Amount of space to reserve at the end of each line */ private int lineEndBorderAndPaddingWidth; private int breakBefore; private int breakAfter; private Keep pendingKeepWithNext = Keep.KEEP_AUTO; private Keep pendingKeepWithPrevious = Keep.KEEP_AUTO; private int disableColumnBalancing; public static LayoutContext newInstance() { return new LayoutContext(0); } public static LayoutContext copyOf(LayoutContext copy) { return new LayoutContext(copy); } /** * Returns a descendant of the given layout context. The new context is the same as * what would have been created by {@link #newInstance()}, except for inheritable * properties that are passed on by the parent. At the moment, the only inheritable * property is the value returned by {@link #treatAsArtifact()}. */ public static LayoutContext offspringOf(LayoutContext parent) { LayoutContext offspring = new LayoutContext(0); offspring.setTreatAsArtifact(parent.treatAsArtifact()); return offspring; } private LayoutContext(LayoutContext parentLC) { this.flags = parentLC.flags; this.refIPD = parentLC.refIPD; this.writingMode = parentLC.writingMode; setStackLimitBP(parentLC.getStackLimitBP()); this.leadingSpace = parentLC.leadingSpace; //??? this.trailingSpace = parentLC.trailingSpace; //??? this.hyphContext = parentLC.hyphContext; this.bpAlignment = parentLC.bpAlignment; this.dSpaceAdjust = parentLC.dSpaceAdjust; this.ipdAdjust = parentLC.ipdAdjust; this.alignmentContext = parentLC.alignmentContext; this.lineStartBorderAndPaddingWidth = parentLC.lineStartBorderAndPaddingWidth; this.lineEndBorderAndPaddingWidth = parentLC.lineEndBorderAndPaddingWidth; copyPendingMarksFrom(parentLC); this.pendingKeepWithNext = parentLC.pendingKeepWithNext; this.pendingKeepWithPrevious = parentLC.pendingKeepWithPrevious; // Copy other fields as necessary. this.disableColumnBalancing = parentLC.disableColumnBalancing; } private LayoutContext(int flags) { this.flags = flags; this.refIPD = 0; stackLimitBP = MinOptMax.ZERO; leadingSpace = null; trailingSpace = null; } /** @param source from which pending marks are copied */ public void copyPendingMarksFrom(LayoutContext source) { if (source.pendingAfterMarks != null) { this.pendingAfterMarks = new java.util.ArrayList(source.pendingAfterMarks); } if (source.pendingBeforeMarks != null) { this.pendingBeforeMarks = new java.util.ArrayList(source.pendingBeforeMarks); } } /** @param flags to set */ public void setFlags(int flags) { setFlags(flags, true); } /** * @param flags to set or clear * @param bSet true to set, false to clear */ public void setFlags(int flags, boolean bSet) { if (bSet) { this.flags |= flags; } else { this.flags &= ~flags; } } /** @param flags to clear */ public void unsetFlags(int flags) { setFlags(flags, false); } /** @return true if new area is set */ public boolean isStart() { return ((this.flags & NEW_AREA) != 0); } /** @return true if new area is set and leading space is non-null */ public boolean startsNewArea() { return ((this.flags & NEW_AREA) != 0 && leadingSpace != null); } /** @return true if first area is set */ public boolean isFirstArea() { return ((this.flags & FIRST_AREA) != 0); } /** @return true if last area is set */ public boolean isLastArea() { return ((this.flags & LAST_AREA) != 0); } /** @return true if suppress break before is set */ public boolean suppressBreakBefore() { return ((this.flags & SUPPRESS_BREAK_BEFORE) != 0); } /** * Returns the strength of a keep-with-next currently pending. * @return the keep-with-next strength */ public Keep getKeepWithNextPending() { return this.pendingKeepWithNext; } /** * Returns the strength of a keep-with-previous currently pending. * @return the keep-with-previous strength */ public Keep getKeepWithPreviousPending() { return this.pendingKeepWithPrevious; } /** * Clears any pending keep-with-next strength. */ public void clearKeepWithNextPending() { this.pendingKeepWithNext = Keep.KEEP_AUTO; } /** * Clears any pending keep-with-previous strength. */ public void clearKeepWithPreviousPending() { this.pendingKeepWithPrevious = Keep.KEEP_AUTO; } /** * Clears both keep-with-previous and keep-with-next strengths. */ public void clearKeepsPending() { clearKeepWithPreviousPending(); clearKeepWithNextPending(); } /** * Updates the currently pending keep-with-next strength. * @param keep the new strength to consider */ public void updateKeepWithNextPending(Keep keep) { this.pendingKeepWithNext = this.pendingKeepWithNext.compare(keep); } /** * Updates the currently pending keep-with-previous strength. * @param keep the new strength to consider */ public void updateKeepWithPreviousPending(Keep keep) { this.pendingKeepWithPrevious = this.pendingKeepWithPrevious.compare(keep); } /** * Indicates whether a keep-with-next constraint is pending. * @return true if a keep-with-next constraint is pending */ public boolean isKeepWithNextPending() { return !getKeepWithNextPending().isAuto(); } /** * Indicates whether a keep-with-previous constraint is pending. * @return true if a keep-with-previous constraint is pending */ public boolean isKeepWithPreviousPending() { return !getKeepWithPreviousPending().isAuto(); } /** @param space leading space */ public void setLeadingSpace(SpaceSpecifier space) { leadingSpace = space; } /** @return leading space */ public SpaceSpecifier getLeadingSpace() { return leadingSpace; } /** @return true if resolve leading space is set */ public boolean resolveLeadingSpace() { return ((this.flags & RESOLVE_LEADING_SPACE) != 0); } /** @param space trailing space */ public void setTrailingSpace(SpaceSpecifier space) { trailingSpace = space; } /** @return trailing space */ public SpaceSpecifier getTrailingSpace() { return trailingSpace; } /** * Adds a border or padding element to the pending list which will be used to generate * the right element list for break possibilities. Conditionality resolution will be done * elsewhere. * @param element the border, padding or space element */ public void addPendingAfterMark(UnresolvedListElementWithLength element) { if (this.pendingAfterMarks == null) { this.pendingAfterMarks = new java.util.ArrayList(); } this.pendingAfterMarks.add(element); } /** * @return the pending border and padding elements at the after edge * @see #addPendingAfterMark(UnresolvedListElementWithLength) */ public List getPendingAfterMarks() { if (this.pendingAfterMarks != null) { return Collections.unmodifiableList(this.pendingAfterMarks); } else { return null; } } /** * Clears all pending marks on the LayoutContext. */ public void clearPendingMarks() { this.pendingBeforeMarks = null; this.pendingAfterMarks = null; } /** * Adds a border or padding element to the pending list which will be used to generate * the right element list for break possibilities. Conditionality resolution will be done * elsewhere. * @param element the border, padding or space element */ public void addPendingBeforeMark(UnresolvedListElementWithLength element) { if (this.pendingBeforeMarks == null) { this.pendingBeforeMarks = new java.util.ArrayList(); } this.pendingBeforeMarks.add(element); } /** * @return the pending border and padding elements at the before edge * @see #addPendingBeforeMark(UnresolvedListElementWithLength) */ public List getPendingBeforeMarks() { if (this.pendingBeforeMarks != null) { return Collections.unmodifiableList(this.pendingBeforeMarks); } else { return null; } } /** * Sets the stack limit in block-progression-dimension. * @param limit the stack limit */ public void setStackLimitBP(MinOptMax limit) { stackLimitBP = limit; } /** * Returns the stack limit in block-progression-dimension. * @return the stack limit */ public MinOptMax getStackLimitBP() { return stackLimitBP; } /** * Sets the inline-progression-dimension of the nearest ancestor reference area. * @param ipd of nearest ancestor reference area */ public void setRefIPD(int ipd) { refIPD = ipd; } /** * Returns the inline-progression-dimension of the nearest ancestor reference area. * * @return the inline-progression-dimension of the nearest ancestor reference area */ public int getRefIPD() { return refIPD; } /** @param hyph a hyphenation context */ public void setHyphContext(HyphContext hyph) { hyphContext = hyph; } /** @return hyphenation context */ public HyphContext getHyphContext() { return hyphContext; } /** * Sets the currently applicable alignment in BP direction. * @param alignment one of EN_START, EN_JUSTIFY etc. */ public void setBPAlignment(int alignment) { this.bpAlignment = alignment; } /** @return the currently applicable alignment in BP direction (EN_START, EN_JUSTIFY...) */ public int getBPAlignment() { return this.bpAlignment; } /** @param adjust space adjustment */ public void setSpaceAdjust(double adjust) { dSpaceAdjust = adjust; } /** @return space adjustment */ public double getSpaceAdjust() { return dSpaceAdjust; } /** @param ipdA ipd adjustment */ public void setIPDAdjust(double ipdA) { ipdAdjust = ipdA; } /** @return ipd adjustment */ public double getIPDAdjust() { return ipdAdjust; } /** @param alignmentContext alignment context */ public void setAlignmentContext(AlignmentContext alignmentContext) { this.alignmentContext = alignmentContext; } /** @return alignment context */ public AlignmentContext getAlignmentContext() { return this.alignmentContext; } /** * Reset alignment context. */ public void resetAlignmentContext() { if (this.alignmentContext != null) { this.alignmentContext = this.alignmentContext.getParentAlignmentContext(); } } /** * Get the width to be reserved for border and padding at the start of the line. * @return the width to be reserved */ public int getLineStartBorderAndPaddingWidth() { return lineStartBorderAndPaddingWidth; } /** * Set the width to be reserved for border and padding at the start of the line. * @param lineStartBorderAndPaddingWidth the width to be reserved */ public void setLineStartBorderAndPaddingWidth(int lineStartBorderAndPaddingWidth) { this.lineStartBorderAndPaddingWidth = lineStartBorderAndPaddingWidth; } /** * Get the width to be reserved for border and padding at the end of the line. * @return the width to be reserved */ public int getLineEndBorderAndPaddingWidth() { return lineEndBorderAndPaddingWidth; } /** * Set the width to be reserved for border and padding at the end of the line. * @param lineEndBorderAndPaddingWidth the width to be reserved */ public void setLineEndBorderAndPaddingWidth(int lineEndBorderAndPaddingWidth) { this.lineEndBorderAndPaddingWidth = lineEndBorderAndPaddingWidth; } /** * @return one of: {@link Constants#NOT_SET}, {@link Constants#EN_NONE} * {@link Constants#EN_ALL} */ public int getNextSpan() { return nextSpan; } /** * @return one of: {@link Constants#NOT_SET}, {@link Constants#EN_NONE} * {@link Constants#EN_ALL} */ public int getCurrentSpan() { return (currentSpan == Constants.NOT_SET) ? Constants.EN_NONE : currentSpan; } /** * Used to signal the PSLM that the element list ends early because of a span change in * multi-column layout. * @param span the new span value (legal values: NOT_SET, EN_NONE, EN_ALL) */ public void signalSpanChange(int span) { switch (span) { case Constants.NOT_SET: case Constants.EN_NONE: case Constants.EN_ALL: this.currentSpan = this.nextSpan; this.nextSpan = span; break; default: assert false; throw new IllegalArgumentException("Illegal value on signalSpanChange() for span: " + span); } } /** * Get the writing mode of the relevant reference area. * @return the applicable writing mode */ public WritingMode getWritingMode() { return writingMode; } /** * Set the writing mode. * @param writingMode the writing mode */ public void setWritingMode(WritingMode writingMode) { this.writingMode = writingMode; } /** * Get the current amount of space before / start * @return the space before / start amount */ public int getSpaceBefore() { return spaceBefore; } /** * Set the amount of space before / start * @param spaceBefore the amount of space before / start */ public void setSpaceBefore(int spaceBefore) { this.spaceBefore = spaceBefore; } /** * Get the current amount of space after / end * @return the space after / end amount */ public int getSpaceAfter() { return spaceAfter; } /** * Set the amount of space after / end * @param spaceAfter the amount of space after / end */ public void setSpaceAfter(int spaceAfter) { this.spaceAfter = spaceAfter; } /** * Returns the value of the break before the element whose * {@link LayoutManager#getNextKnuthElements(LayoutContext, int)} method has just been * called. * * @return one of {@link Constants#EN_AUTO}, {@link Constants#EN_COLUMN}, * {@link Constants#EN_PAGE}, {@link Constants#EN_EVEN_PAGE}, or * {@link Constants#EN_ODD_PAGE} */ public int getBreakBefore() { return breakBefore; } /** * Sets the value of the break before the current element. * * @param breakBefore the value of the break-before * @see #getBreakBefore() */ public void setBreakBefore(int breakBefore) { this.breakBefore = breakBefore; } /** * Returns the value of the break after the element whose * {@link LayoutManager#getNextKnuthElements(LayoutContext, int)} method has just been * called. * * @return one of {@link Constants#EN_AUTO}, {@link Constants#EN_COLUMN}, * {@link Constants#EN_PAGE}, {@link Constants#EN_EVEN_PAGE}, or * {@link Constants#EN_ODD_PAGE} */ public int getBreakAfter() { return breakAfter; } /** * Sets the value of the break after the current element. * * @param breakAfter the value of the break-after * @see #getBreakAfter() */ public void setBreakAfter(int breakAfter) { this.breakAfter = breakAfter; } /** {@inheritDoc} */ @Override public String toString() { return "Layout Context:" + "\nStack Limit BPD: \t" + (getStackLimitBP() == null ? "null" : getStackLimitBP().toString()) + "\nTrailing Space: \t" + (getTrailingSpace() == null ? "null" : getTrailingSpace().toString()) + "\nLeading Space: \t" + (getLeadingSpace() == null ? "null" : getLeadingSpace().toString()) + "\nReference IPD: \t" + getRefIPD() + "\nSpace Adjust: \t" + getSpaceAdjust() + "\nIPD Adjust: \t" + getIPDAdjust() + "\nResolve Leading Space: \t" + resolveLeadingSpace() + "\nSuppress Break Before: \t" + suppressBreakBefore() + "\nIs First Area: \t" + isFirstArea() + "\nStarts New Area: \t" + startsNewArea() + "\nIs Last Area: \t" + isLastArea() + "\nKeeps: \t[keep-with-next=" + getKeepWithNextPending() + "][keep-with-previous=" + getKeepWithPreviousPending() + "] pending" + "\nBreaks: \tforced [" + (breakBefore != Constants.EN_AUTO ? "break-before" : "") + "][" + (breakAfter != Constants.EN_AUTO ? "break-after" : "") + "]"; } /** * Returns whether the column balancer should be disabled before a spanning block * * @return one of {@link Constants#EN_TRUE}, {@link Constants#EN_FALSE} */ public int getDisableColumnBalancing() { return disableColumnBalancing; } /** * Sets whether the column balancer should be disabled before a spanning block * * @param disableColumnBalancing the value of the fox:disable-column-balancing property * @see #getDisableColumnBalancing() */ public void setDisableColumnBalancing(int disableColumnBalancing) { this.disableColumnBalancing = disableColumnBalancing; } public boolean treatAsArtifact() { return (flags & TREAT_AS_ARTIFACT) != 0; } public void setTreatAsArtifact(boolean treatAsArtifact) { setFlags(TREAT_AS_ARTIFACT, treatAsArtifact); } }