/* * 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 org.apache.fop.datatypes.Length; import org.apache.fop.datatypes.LengthBase; import org.apache.fop.datatypes.SimplePercentBaseContext; import org.apache.fop.fo.Constants; import org.apache.fop.fonts.Font; /** * The alignment context is carried within a LayoutContext and as * part of the Knuth Inline elements to facilitate proper line building. * All measurements are in mpt. */ public class AlignmentContext implements Constants { /** The height or BPD of this context */ private int areaHeight; /** The computed line-height property value applicable */ private int lineHeight; /** The distance in BPD from the top of the box to the alignmentPoint */ private int alignmentPoint; /** The baseline shift value in effect */ private int baselineShiftValue; /** The computed alignment baseline identifier */ private int alignmentBaselineIdentifier; /** The x height */ private int xHeight; private ScaledBaselineTable scaledBaselineTable = null; private ScaledBaselineTable actualBaselineTable = null; private AlignmentContext parentAlignmentContext = null; /** * Creates a new instance of AlignmentContext * for graphics areas. * @param height the total height of the area * @param alignmentAdjust the alignment-adjust property * @param alignmentBaseline the alignment-baseline property * @param baselineShift the baseline-shift property * @param dominantBaseline the dominant-baseline property * @param parentAlignmentContext the parent alignment context */ public AlignmentContext(int height , Length alignmentAdjust , int alignmentBaseline , Length baselineShift , int dominantBaseline , AlignmentContext parentAlignmentContext) { this.areaHeight = height; this.lineHeight = height; this.xHeight = height; this.parentAlignmentContext = parentAlignmentContext; this.scaledBaselineTable = parentAlignmentContext.getScaledBaselineTable(); setAlignmentBaselineIdentifier(alignmentBaseline , parentAlignmentContext.getDominantBaselineIdentifier()); setBaselineShift(baselineShift); int dominantBaselineIdentifier = parentAlignmentContext.getDominantBaselineIdentifier(); boolean newScaledBaselineTableRequired = false; if (baselineShiftValue != 0) { newScaledBaselineTableRequired = true; } switch (dominantBaseline) { case EN_AUTO: newScaledBaselineTableRequired = baselineShiftValue != 0; break; case EN_USE_SCRIPT: // TODO break; case EN_NO_CHANGE: break; case EN_RESET_SIZE: newScaledBaselineTableRequired = true; break; default: newScaledBaselineTableRequired = true; dominantBaselineIdentifier = dominantBaseline; break; } actualBaselineTable = ScaledBaselineTableFactory.makeGraphicsScaledBaselineTable( height, dominantBaselineIdentifier, scaledBaselineTable.getWritingMode()); if (newScaledBaselineTableRequired) { scaledBaselineTable = ScaledBaselineTableFactory.makeGraphicsScaledBaselineTable( height, dominantBaselineIdentifier, scaledBaselineTable.getWritingMode()); } setAlignmentAdjust(alignmentAdjust); } /** * Creates a new instance of AlignmentContext * @param font the font * @param lineHeight the computed value of the lineHeight property * @param alignmentAdjust the alignment-adjust property * @param alignmentBaseline the alignment-baseline property * @param baselineShift the baseline-shift property * @param dominantBaseline the dominant-baseline property * @param parentAlignmentContext the parent alignment context */ public AlignmentContext(Font font , int lineHeight , Length alignmentAdjust , int alignmentBaseline , Length baselineShift , int dominantBaseline , AlignmentContext parentAlignmentContext) { this.areaHeight = font.getAscender() - font.getDescender(); this.lineHeight = lineHeight; this.parentAlignmentContext = parentAlignmentContext; this.scaledBaselineTable = parentAlignmentContext.getScaledBaselineTable(); this.xHeight = font.getXHeight(); setAlignmentBaselineIdentifier(alignmentBaseline , parentAlignmentContext.getDominantBaselineIdentifier()); setBaselineShift(baselineShift); int dominantBaselineIdentifier = parentAlignmentContext.getDominantBaselineIdentifier(); boolean newScaledBaselineTableRequired = false; if (baselineShiftValue != 0) { newScaledBaselineTableRequired = true; } switch (dominantBaseline) { case EN_AUTO: newScaledBaselineTableRequired = baselineShiftValue != 0; break; case EN_USE_SCRIPT: // TODO break; case EN_NO_CHANGE: break; case EN_RESET_SIZE: newScaledBaselineTableRequired = true; break; default: newScaledBaselineTableRequired = true; dominantBaselineIdentifier = dominantBaseline; break; } actualBaselineTable = ScaledBaselineTableFactory.makeFontScaledBaselineTable(font, dominantBaselineIdentifier, scaledBaselineTable.getWritingMode()); if (newScaledBaselineTableRequired) { scaledBaselineTable = ScaledBaselineTableFactory.makeFontScaledBaselineTable(font, dominantBaselineIdentifier, scaledBaselineTable.getWritingMode()); } setAlignmentAdjust(alignmentAdjust); } /** * Creates a new instance of AlignmentContext based simply * on the font and the writing mode. * @param font the font * @param lineHeight the computed value of the lineHeight property * @param writingMode the current writing mode */ public AlignmentContext(Font font, int lineHeight, int writingMode) { this.areaHeight = font.getAscender() - font.getDescender(); this.lineHeight = lineHeight; this.xHeight = font.getXHeight(); this.parentAlignmentContext = null; this.scaledBaselineTable = ScaledBaselineTableFactory.makeFontScaledBaselineTable(font, writingMode); this.actualBaselineTable = scaledBaselineTable; this.alignmentBaselineIdentifier = getDominantBaselineIdentifier(); this.alignmentPoint = font.getAscender(); this.baselineShiftValue = 0; } /** * Returns the alignment point for this context. * This is the point on the start edge of the area this context * applies to measured from the before edge of the area. * @return the default alignment point */ public int getAlignmentPoint() { return alignmentPoint; } /** * Returns the current value of baseline shift in effect. * @return the baseline shift */ public int getBaselineShiftValue() { return baselineShiftValue; } /** * Returns the current alignment baseline identifier * @return the alignment baseline identifier */ public int getAlignmentBaselineIdentifier() { return alignmentBaselineIdentifier; } /** * Sets the current alignment baseline identifer. For * alignment-baseline values of "auto" and "baseline" this * method does the conversion into the appropriate computed * value assuming script is "auto" and the fo is not fo:character. * @param alignmentBaseline the alignment-baseline property * @param parentDominantBaselineIdentifier the dominant baseline of the parent fo */ private void setAlignmentBaselineIdentifier(int alignmentBaseline , int parentDominantBaselineIdentifier) { switch (alignmentBaseline) { case EN_AUTO: // fall through case EN_BASELINE: this.alignmentBaselineIdentifier = parentDominantBaselineIdentifier; break; case EN_BEFORE_EDGE: case EN_TEXT_BEFORE_EDGE: case EN_CENTRAL: case EN_MIDDLE: case EN_AFTER_EDGE: case EN_TEXT_AFTER_EDGE: case EN_IDEOGRAPHIC: case EN_ALPHABETIC: case EN_HANGING: case EN_MATHEMATICAL: this.alignmentBaselineIdentifier = alignmentBaseline; break; } } /** * Sets the current alignment baseline identifer. For * alignment-baseline values of "auto" and "baseline" this * method does the conversion into the appropriate computed * value assuming script is "auto" and the fo is not fo:character. * @param alignmentAdjust the alignment-adjust property */ private void setAlignmentAdjust(Length alignmentAdjust) { int beforeEdge = actualBaselineTable.getBaseline(EN_BEFORE_EDGE); switch (alignmentAdjust.getEnum()) { case EN_AUTO: alignmentPoint = beforeEdge - actualBaselineTable.getBaseline(alignmentBaselineIdentifier); break; case EN_BASELINE: alignmentPoint = beforeEdge; break; case EN_BEFORE_EDGE: case EN_TEXT_BEFORE_EDGE: case EN_CENTRAL: case EN_MIDDLE: case EN_AFTER_EDGE: case EN_TEXT_AFTER_EDGE: case EN_IDEOGRAPHIC: case EN_ALPHABETIC: case EN_HANGING: case EN_MATHEMATICAL: alignmentPoint = beforeEdge - actualBaselineTable.getBaseline(alignmentAdjust.getEnum()); break; default: alignmentPoint = beforeEdge + alignmentAdjust.getValue(new SimplePercentBaseContext(null , LengthBase.ALIGNMENT_ADJUST , lineHeight)); break; } } /** * Return the scaled baseline table for this context. * @return the scaled baseline table */ public ScaledBaselineTable getScaledBaselineTable() { return this.scaledBaselineTable; } /** * Return the dominant baseline identifier. * @return the dominant baseline identifier */ public int getDominantBaselineIdentifier() { return scaledBaselineTable.getDominantBaselineIdentifier(); } /** * Return the writing mode. * @return the writing mode */ public int getWritingMode() { return scaledBaselineTable.getWritingMode(); } /** * Calculates the baseline shift value based on the baseline-shift * property value. * @param baselineShift the baseline shift property value * @return the computed baseline shift value */ private void setBaselineShift(Length baselineShift) { baselineShiftValue = 0; ScaledBaselineTable sbt = null; switch (baselineShift.getEnum()) { case EN_BASELINE: //Nothing to do break; case EN_SUB: baselineShiftValue = Math.round(-(xHeight / 2) + parentAlignmentContext.getActualBaselineOffset(EN_ALPHABETIC) ); break; case EN_SUPER: baselineShiftValue = Math.round(parentAlignmentContext.getXHeight() + parentAlignmentContext.getActualBaselineOffset(EN_ALPHABETIC) ); break; case 0: // A or value baselineShiftValue = baselineShift.getValue( new SimplePercentBaseContext(null , LengthBase.CUSTOM_BASE , parentAlignmentContext.getLineHeight())); break; } } /** * Return the parent alignment context. * @return the parent alignment context */ public AlignmentContext getParentAlignmentContext() { return parentAlignmentContext; } /** * Return the offset between the current dominant baseline and * the parent dominant baseline. * @return the offset in shift direction */ public int getBaselineOffset() { if (parentAlignmentContext == null) { return 0; } return parentAlignmentContext.getScaledBaselineTable() .getBaseline(alignmentBaselineIdentifier) - scaledBaselineTable .deriveScaledBaselineTable(parentAlignmentContext.getDominantBaselineIdentifier()) .getBaseline(alignmentBaselineIdentifier) - scaledBaselineTable .getBaseline(parentAlignmentContext.getDominantBaselineIdentifier()) + baselineShiftValue; } /** * Return the offset between the current dominant baseline and * the outermost parent dominant baseline. * @return the offet in shift direction */ public int getTotalBaselineOffset() { int offset = 0; if (parentAlignmentContext != null) { offset = getBaselineOffset() + parentAlignmentContext.getTotalBaselineOffset(); } return offset; } /** * Return the offset between the alignment baseline and * the outermost parent dominant baseline. * @return the offset in shift direction */ public int getTotalAlignmentBaselineOffset() { return getTotalAlignmentBaselineOffset(alignmentBaselineIdentifier); } /** * Return the offset between the given alignment baseline and * the outermost parent dominant baseline. * @param alignmentBaselineId the alignment baseline * @return the offset */ public int getTotalAlignmentBaselineOffset(int alignmentBaselineId) { int offset = baselineShiftValue; if (parentAlignmentContext != null) { offset = parentAlignmentContext.getTotalBaselineOffset() + parentAlignmentContext.getScaledBaselineTable() .getBaseline(alignmentBaselineId) + baselineShiftValue; } return offset; } /** * Return the offset between the dominant baseline and * the given actual baseline * @param baselineIdentifier the baseline * @return the offset */ public int getActualBaselineOffset(int baselineIdentifier) { // This is the offset from the dominant baseline to the alignment baseline int offset = getTotalAlignmentBaselineOffset() - getTotalBaselineOffset(); // Add the offset to the actual baseline we want offset += actualBaselineTable.deriveScaledBaselineTable(alignmentBaselineIdentifier) .getBaseline(baselineIdentifier); return offset; } /** * Return the offset the outermost parent dominant baseline * and the top of this box. * @return the offset */ private int getTotalTopOffset() { int offset = getTotalAlignmentBaselineOffset() + getAltitude(); return offset; } /** * Return the total height of the context. * @return the height */ public int getHeight() { return areaHeight; } /** * Return the line height of the context. * @return the height */ public int getLineHeight() { return lineHeight; } /** * The altitude of the context that is the height above the * alignment point. * @return the altitude */ public int getAltitude() { return alignmentPoint; } /** * The depth of the context that is the height below * alignment point. * @return the altitude */ public int getDepth() { return getHeight() - alignmentPoint; } /** * The x height of the context. * @return the x height */ public int getXHeight() { return this.xHeight; } /** * Resizes the line as specified. Assumes that the new alignment point * is on the dominant baseline, that is this function should be called for * line areas only. * @param newLineHeight the new height of the line * @param newAlignmentPoint the new alignment point */ public void resizeLine(int newLineHeight, int newAlignmentPoint) { areaHeight = newLineHeight; alignmentPoint = newAlignmentPoint; scaledBaselineTable.setBeforeAndAfterBaselines(alignmentPoint , alignmentPoint - areaHeight); } /** * Returns the offset from the before-edge of the parent to * this context. * @return the offset for rendering */ public int getOffset() { int offset = 0; if (parentAlignmentContext != null) { offset = parentAlignmentContext.getTotalTopOffset() - getTotalTopOffset(); } else { offset = getAltitude() - scaledBaselineTable.getBaseline(EN_TEXT_BEFORE_EDGE); } return offset; } /** * Returns an indication if we still use the initial baseline table. * The initial baseline table is the table generated by the Line LM. * @return true if this is still the initial baseline table */ public boolean usesInitialBaselineTable() { return parentAlignmentContext == null || (scaledBaselineTable == parentAlignmentContext.getScaledBaselineTable() && parentAlignmentContext.usesInitialBaselineTable()); } private boolean isHorizontalWritingMode() { return (getWritingMode() == EN_LR_TB || getWritingMode() == EN_RL_TB); } /** {@inheritDoc} */ public String toString() { StringBuffer sb = new StringBuffer(64); sb.append("ah=" + areaHeight); sb.append(" lp=" + lineHeight); sb.append(" ap=" + alignmentPoint); sb.append(" ab=" + alignmentBaselineIdentifier); sb.append(" bs=" + baselineShiftValue); return sb.toString(); } }