/* * 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. */ /** * @author Oleg V. Khaschansky * @version $Revision$ * */ package org.apache.harmony.awt.gl.font; import java.awt.font.LineMetrics; import java.awt.font.GraphicAttribute; import java.awt.Font; import java.util.HashMap; import java.util.ArrayList; import org.apache.harmony.awt.internal.nls.Messages; /** * This class operates with an arbitrary text string which can include * any number of style, font and direction runs. It is responsible for computation * of the text metrics, such as ascent, descent, leading and advance. Actually, * each text run segment contains logic which allows it to compute its own metrics and * responsibility of this class is to combine metrics for all segments included in the text, * managed by the associated TextRunBreaker object. */ public class TextMetricsCalculator { TextRunBreaker breaker; // Associated run breaker // Metrics float ascent = 0; float descent = 0; float leading = 0; float advance = 0; private float baselineOffsets[]; int baselineIndex; public TextMetricsCalculator(TextRunBreaker breaker) { this.breaker = breaker; checkBaselines(); } /** * Returns either values cached by checkBaselines method or reasonable * values for the TOP and BOTTOM alignments. * @param baselineIndex - baseline index * @return baseline offset */ float getBaselineOffset(int baselineIndex) { if (baselineIndex >= 0) { return baselineOffsets[baselineIndex]; } else if (baselineIndex == GraphicAttribute.BOTTOM_ALIGNMENT) { return descent; } else if (baselineIndex == GraphicAttribute.TOP_ALIGNMENT) { return -ascent; } else { // awt.3F=Invalid baseline index throw new IllegalArgumentException(Messages.getString("awt.3F")); //$NON-NLS-1$ } } public float[] getBaselineOffsets() { float ret[] = new float[baselineOffsets.length]; System.arraycopy(baselineOffsets, 0, ret, 0, baselineOffsets.length); return ret; } /** * Take baseline offsets from the first font or graphic attribute * and normalizes them, than caches the results. */ public void checkBaselines() { // Take baseline offsets of the first font and normalize them HashMap<Integer, Font> fonts = breaker.fonts; Object val = fonts.get(new Integer(0)); if (val instanceof Font) { Font firstFont = (Font) val; LineMetrics lm = firstFont.getLineMetrics(breaker.text, 0, 1, breaker.frc); baselineOffsets = lm.getBaselineOffsets(); baselineIndex = lm.getBaselineIndex(); } else if (val instanceof GraphicAttribute) { // Get first graphic attribute and use it GraphicAttribute ga = (GraphicAttribute) val; int align = ga.getAlignment(); if ( align == GraphicAttribute.TOP_ALIGNMENT || align == GraphicAttribute.BOTTOM_ALIGNMENT ) { baselineIndex = GraphicAttribute.ROMAN_BASELINE; } else { baselineIndex = align; } baselineOffsets = new float[3]; baselineOffsets[0] = 0; baselineOffsets[1] = (ga.getDescent() - ga.getAscent()) / 2.f; baselineOffsets[2] = -ga.getAscent(); } else { // Use defaults - Roman baseline and zero offsets baselineIndex = GraphicAttribute.ROMAN_BASELINE; baselineOffsets = new float[3]; } // Normalize offsets if needed if (baselineOffsets[baselineIndex] != 0) { float baseOffset = baselineOffsets[baselineIndex]; for (int i = 0; i < baselineOffsets.length; i++) { baselineOffsets[i] -= baseOffset; } } } /** * Computes metrics for the text managed by the associated TextRunBreaker */ void computeMetrics() { ArrayList<TextRunSegment> segments = breaker.runSegments; float maxHeight = 0; float maxHeightLeading = 0; for (int i = 0; i < segments.size(); i++) { TextRunSegment segment = segments.get(i); BasicMetrics metrics = segment.metrics; int baseline = metrics.baseLineIndex; if (baseline >= 0) { float baselineOffset = baselineOffsets[metrics.baseLineIndex]; float fixedDescent = metrics.descent + baselineOffset; ascent = Math.max(ascent, metrics.ascent - baselineOffset); descent = Math.max(descent, fixedDescent); leading = Math.max(leading, fixedDescent + metrics.leading); } else { // Position is not fixed by the baseline, need sum of ascent and descent float height = metrics.ascent + metrics.descent; maxHeight = Math.max(maxHeight, height); maxHeightLeading = Math.max(maxHeightLeading, height + metrics.leading); } } // Need to increase sizes for graphics? if (maxHeightLeading != 0) { descent = Math.max(descent, maxHeight - ascent); leading = Math.max(leading, maxHeightLeading - ascent); } // Normalize leading leading -= descent; BasicMetrics currMetrics; float currAdvance = 0; for (int i = 0; i < segments.size(); i++) { TextRunSegment segment = segments.get(breaker.getSegmentFromVisualOrder(i)); currMetrics = segment.metrics; segment.y = getBaselineOffset(currMetrics.baseLineIndex) + currMetrics.superScriptOffset; segment.x = currAdvance; currAdvance += segment.getAdvance(); } advance = currAdvance; } /** * Computes metrics and creates BasicMetrics object from them * @return basic metrics */ public BasicMetrics createMetrics() { computeMetrics(); return new BasicMetrics(this); } /** * Corrects advance after justification. Gets BasicMetrics object * and updates advance stored into it. * @param metrics - metrics with outdated advance which should be corrected */ public void correctAdvance(BasicMetrics metrics) { ArrayList<TextRunSegment> segments = breaker.runSegments; TextRunSegment segment = segments.get(breaker .getSegmentFromVisualOrder(segments.size() - 1)); advance = segment.x + segment.getAdvance(); metrics.advance = advance; } }