blob: 4eb0abd641a2fe82b52693bcb77c8c010886cb1e [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.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 <length> or <percentage> 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();
}
}