1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17/** 18 * @author Oleg V. Khaschansky 19 * @version $Revision$ 20 * 21 */ 22 23package org.apache.harmony.awt.gl.font; 24 25import java.awt.font.LineMetrics; 26import java.awt.font.GraphicAttribute; 27import java.awt.Font; 28import java.util.HashMap; 29import java.util.ArrayList; 30 31import org.apache.harmony.awt.internal.nls.Messages; 32 33/** 34 * This class operates with an arbitrary text string which can include 35 * any number of style, font and direction runs. It is responsible for computation 36 * of the text metrics, such as ascent, descent, leading and advance. Actually, 37 * each text run segment contains logic which allows it to compute its own metrics and 38 * responsibility of this class is to combine metrics for all segments included in the text, 39 * managed by the associated TextRunBreaker object. 40 */ 41public class TextMetricsCalculator { 42 TextRunBreaker breaker; // Associated run breaker 43 44 // Metrics 45 float ascent = 0; 46 float descent = 0; 47 float leading = 0; 48 float advance = 0; 49 50 private float baselineOffsets[]; 51 int baselineIndex; 52 53 public TextMetricsCalculator(TextRunBreaker breaker) { 54 this.breaker = breaker; 55 checkBaselines(); 56 } 57 58 /** 59 * Returns either values cached by checkBaselines method or reasonable 60 * values for the TOP and BOTTOM alignments. 61 * @param baselineIndex - baseline index 62 * @return baseline offset 63 */ 64 float getBaselineOffset(int baselineIndex) { 65 if (baselineIndex >= 0) { 66 return baselineOffsets[baselineIndex]; 67 } else if (baselineIndex == GraphicAttribute.BOTTOM_ALIGNMENT) { 68 return descent; 69 } else if (baselineIndex == GraphicAttribute.TOP_ALIGNMENT) { 70 return -ascent; 71 } else { 72 // awt.3F=Invalid baseline index 73 throw new IllegalArgumentException(Messages.getString("awt.3F")); //$NON-NLS-1$ 74 } 75 } 76 77 public float[] getBaselineOffsets() { 78 float ret[] = new float[baselineOffsets.length]; 79 System.arraycopy(baselineOffsets, 0, ret, 0, baselineOffsets.length); 80 return ret; 81 } 82 83 /** 84 * Take baseline offsets from the first font or graphic attribute 85 * and normalizes them, than caches the results. 86 */ 87 public void checkBaselines() { 88 // Take baseline offsets of the first font and normalize them 89 HashMap<Integer, Font> fonts = breaker.fonts; 90 91 Object val = fonts.get(new Integer(0)); 92 93 if (val instanceof Font) { 94 Font firstFont = (Font) val; 95 LineMetrics lm = firstFont.getLineMetrics(breaker.text, 0, 1, breaker.frc); 96 baselineOffsets = lm.getBaselineOffsets(); 97 baselineIndex = lm.getBaselineIndex(); 98 } else if (val instanceof GraphicAttribute) { 99 // Get first graphic attribute and use it 100 GraphicAttribute ga = (GraphicAttribute) val; 101 102 int align = ga.getAlignment(); 103 104 if ( 105 align == GraphicAttribute.TOP_ALIGNMENT || 106 align == GraphicAttribute.BOTTOM_ALIGNMENT 107 ) { 108 baselineIndex = GraphicAttribute.ROMAN_BASELINE; 109 } else { 110 baselineIndex = align; 111 } 112 113 baselineOffsets = new float[3]; 114 baselineOffsets[0] = 0; 115 baselineOffsets[1] = (ga.getDescent() - ga.getAscent()) / 2.f; 116 baselineOffsets[2] = -ga.getAscent(); 117 } else { // Use defaults - Roman baseline and zero offsets 118 baselineIndex = GraphicAttribute.ROMAN_BASELINE; 119 baselineOffsets = new float[3]; 120 } 121 122 // Normalize offsets if needed 123 if (baselineOffsets[baselineIndex] != 0) { 124 float baseOffset = baselineOffsets[baselineIndex]; 125 for (int i = 0; i < baselineOffsets.length; i++) { 126 baselineOffsets[i] -= baseOffset; 127 } 128 } 129 } 130 131 /** 132 * Computes metrics for the text managed by the associated TextRunBreaker 133 */ 134 void computeMetrics() { 135 136 ArrayList<TextRunSegment> segments = breaker.runSegments; 137 138 float maxHeight = 0; 139 float maxHeightLeading = 0; 140 141 for (int i = 0; i < segments.size(); i++) { 142 TextRunSegment segment = segments.get(i); 143 BasicMetrics metrics = segment.metrics; 144 int baseline = metrics.baseLineIndex; 145 146 if (baseline >= 0) { 147 float baselineOffset = baselineOffsets[metrics.baseLineIndex]; 148 float fixedDescent = metrics.descent + baselineOffset; 149 150 ascent = Math.max(ascent, metrics.ascent - baselineOffset); 151 descent = Math.max(descent, fixedDescent); 152 leading = Math.max(leading, fixedDescent + metrics.leading); 153 } else { // Position is not fixed by the baseline, need sum of ascent and descent 154 float height = metrics.ascent + metrics.descent; 155 156 maxHeight = Math.max(maxHeight, height); 157 maxHeightLeading = Math.max(maxHeightLeading, height + metrics.leading); 158 } 159 } 160 161 // Need to increase sizes for graphics? 162 if (maxHeightLeading != 0) { 163 descent = Math.max(descent, maxHeight - ascent); 164 leading = Math.max(leading, maxHeightLeading - ascent); 165 } 166 167 // Normalize leading 168 leading -= descent; 169 170 BasicMetrics currMetrics; 171 float currAdvance = 0; 172 173 for (int i = 0; i < segments.size(); i++) { 174 TextRunSegment segment = segments.get(breaker.getSegmentFromVisualOrder(i)); 175 currMetrics = segment.metrics; 176 177 segment.y = getBaselineOffset(currMetrics.baseLineIndex) 178 + currMetrics.superScriptOffset; 179 segment.x = currAdvance; 180 181 currAdvance += segment.getAdvance(); 182 } 183 184 advance = currAdvance; 185 } 186 187 /** 188 * Computes metrics and creates BasicMetrics object from them 189 * @return basic metrics 190 */ 191 public BasicMetrics createMetrics() { 192 computeMetrics(); 193 return new BasicMetrics(this); 194 } 195 196 /** 197 * Corrects advance after justification. Gets BasicMetrics object 198 * and updates advance stored into it. 199 * @param metrics - metrics with outdated advance which should be corrected 200 */ 201 public void correctAdvance(BasicMetrics metrics) { 202 ArrayList<TextRunSegment> segments = breaker.runSegments; 203 TextRunSegment segment = segments.get(breaker 204 .getSegmentFromVisualOrder(segments.size() - 1)); 205 206 advance = segment.x + segment.getAdvance(); 207 metrics.advance = advance; 208 } 209} 210