001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Oleg V. Khaschansky
019: * @version $Revision$
020: *
021: */package org.apache.harmony.awt.gl.font;
022:
023: import java.awt.font.LineMetrics;
024: import java.awt.font.GraphicAttribute;
025: import java.awt.Font;
026: import java.util.HashMap;
027: import java.util.ArrayList;
028:
029: import org.apache.harmony.awt.internal.nls.Messages;
030:
031: /**
032: * This class operates with an arbitrary text string which can include
033: * any number of style, font and direction runs. It is responsible for computation
034: * of the text metrics, such as ascent, descent, leading and advance. Actually,
035: * each text run segment contains logic which allows it to compute its own metrics and
036: * responsibility of this class is to combine metrics for all segments included in the text,
037: * managed by the associated TextRunBreaker object.
038: */
039: public class TextMetricsCalculator {
040: TextRunBreaker breaker; // Associated run breaker
041:
042: // Metrics
043: float ascent = 0;
044: float descent = 0;
045: float leading = 0;
046: float advance = 0;
047:
048: private float baselineOffsets[];
049: int baselineIndex;
050:
051: public TextMetricsCalculator(TextRunBreaker breaker) {
052: this .breaker = breaker;
053: checkBaselines();
054: }
055:
056: /**
057: * Returns either values cached by checkBaselines method or reasonable
058: * values for the TOP and BOTTOM alignments.
059: * @param baselineIndex - baseline index
060: * @return baseline offset
061: */
062: float getBaselineOffset(int baselineIndex) {
063: if (baselineIndex >= 0) {
064: return baselineOffsets[baselineIndex];
065: } else if (baselineIndex == GraphicAttribute.BOTTOM_ALIGNMENT) {
066: return descent;
067: } else if (baselineIndex == GraphicAttribute.TOP_ALIGNMENT) {
068: return -ascent;
069: } else {
070: // awt.3F=Invalid baseline index
071: throw new IllegalArgumentException(Messages
072: .getString("awt.3F")); //$NON-NLS-1$
073: }
074: }
075:
076: public float[] getBaselineOffsets() {
077: float ret[] = new float[baselineOffsets.length];
078: System.arraycopy(baselineOffsets, 0, ret, 0,
079: baselineOffsets.length);
080: return ret;
081: }
082:
083: /**
084: * Take baseline offsets from the first font or graphic attribute
085: * and normalizes them, than caches the results.
086: */
087: public void checkBaselines() {
088: // Take baseline offsets of the first font and normalize them
089: HashMap<Integer, Object> fonts = breaker.fonts;
090:
091: Object val = fonts.get(new Integer(0));
092:
093: if (val instanceof Font) {
094: Font firstFont = (Font) val;
095: LineMetrics lm = firstFont.getLineMetrics(breaker.text, 0,
096: 1, breaker.frc);
097: baselineOffsets = lm.getBaselineOffsets();
098: baselineIndex = lm.getBaselineIndex();
099: } else if (val instanceof GraphicAttribute) {
100: // Get first graphic attribute and use it
101: GraphicAttribute ga = (GraphicAttribute) val;
102:
103: int align = ga.getAlignment();
104:
105: if (align == GraphicAttribute.TOP_ALIGNMENT
106: || align == GraphicAttribute.BOTTOM_ALIGNMENT) {
107: baselineIndex = GraphicAttribute.ROMAN_BASELINE;
108: } else {
109: baselineIndex = align;
110: }
111:
112: baselineOffsets = new float[3];
113: baselineOffsets[0] = 0;
114: baselineOffsets[1] = (ga.getDescent() - ga.getAscent()) / 2.f;
115: baselineOffsets[2] = -ga.getAscent();
116: } else { // Use defaults - Roman baseline and zero offsets
117: baselineIndex = GraphicAttribute.ROMAN_BASELINE;
118: baselineOffsets = new float[3];
119: }
120:
121: // Normalize offsets if needed
122: if (baselineOffsets[baselineIndex] != 0) {
123: float baseOffset = baselineOffsets[baselineIndex];
124: for (int i = 0; i < baselineOffsets.length; i++) {
125: baselineOffsets[i] -= baseOffset;
126: }
127: }
128: }
129:
130: /**
131: * Computes metrics for the text managed by the associated TextRunBreaker
132: */
133: void computeMetrics() {
134:
135: ArrayList<TextRunSegment> segments = breaker.runSegments;
136:
137: float maxHeight = 0;
138: float maxHeightLeading = 0;
139:
140: for (int i = 0; i < segments.size(); i++) {
141: TextRunSegment segment = segments.get(i);
142: BasicMetrics metrics = segment.metrics;
143: int baseline = metrics.baseLineIndex;
144:
145: if (baseline >= 0) {
146: float baselineOffset = baselineOffsets[metrics.baseLineIndex];
147: float fixedDescent = metrics.descent + baselineOffset;
148:
149: ascent = Math.max(ascent, metrics.ascent
150: - baselineOffset);
151: descent = Math.max(descent, fixedDescent);
152: leading = Math.max(leading, fixedDescent
153: + metrics.leading);
154: } else { // Position is not fixed by the baseline, need sum of ascent and descent
155: float height = metrics.ascent + metrics.descent;
156:
157: maxHeight = Math.max(maxHeight, height);
158: maxHeightLeading = Math.max(maxHeightLeading, height
159: + metrics.leading);
160: }
161: }
162:
163: // Need to increase sizes for graphics?
164: if (maxHeightLeading != 0) {
165: descent = Math.max(descent, maxHeight - ascent);
166: leading = Math.max(leading, maxHeightLeading - ascent);
167: }
168:
169: // Normalize leading
170: leading -= descent;
171:
172: BasicMetrics currMetrics;
173: float currAdvance = 0;
174:
175: for (int i = 0; i < segments.size(); i++) {
176: TextRunSegment segment = segments.get(breaker
177: .getSegmentFromVisualOrder(i));
178: currMetrics = segment.metrics;
179:
180: segment.y = getBaselineOffset(currMetrics.baseLineIndex)
181: + currMetrics.super ScriptOffset;
182: segment.x = currAdvance;
183:
184: currAdvance += segment.getAdvance();
185: }
186:
187: advance = currAdvance;
188: }
189:
190: /**
191: * Computes metrics and creates BasicMetrics object from them
192: * @return basic metrics
193: */
194: public BasicMetrics createMetrics() {
195: computeMetrics();
196: return new BasicMetrics(this );
197: }
198:
199: /**
200: * Corrects advance after justification. Gets BasicMetrics object
201: * and updates advance stored into it.
202: * @param metrics - metrics with outdated advance which should be corrected
203: */
204: public void correctAdvance(BasicMetrics metrics) {
205: ArrayList<TextRunSegment> segments = breaker.runSegments;
206: TextRunSegment segment = segments.get(breaker
207: .getSegmentFromVisualOrder(segments.size() - 1));
208:
209: advance = segment.x + segment.getAdvance();
210: metrics.advance = advance;
211: }
212: }
|