001 /*
002 * Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 /*
027 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
028 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
029 *
030 * The original version of this source code and documentation is
031 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
032 * of IBM. These materials are provided under terms of a License
033 * Agreement between Taligent and Sun. This technology is protected
034 * by multiple US and International patents.
035 *
036 * This notice and attribution to Taligent may not be removed.
037 * Taligent is a registered trademark of Taligent, Inc.
038 *
039 */
040
041 package java.awt.font;
042
043 /*
044 * one info for each side of each glyph
045 * separate infos for grow and shrink case
046 * !!! this doesn't really need to be a separate class. If we keep it
047 * separate, probably the newJustify code from TextLayout belongs here as well.
048 */
049
050 class TextJustifier {
051 private GlyphJustificationInfo[] info;
052 private int start;
053 private int limit;
054
055 static boolean DEBUG = false;
056
057 /**
058 * Initialize the justifier with an array of infos corresponding to each
059 * glyph. Start and limit indicate the range of the array to examine.
060 */
061 TextJustifier(GlyphJustificationInfo[] info, int start, int limit) {
062 this .info = info;
063 this .start = start;
064 this .limit = limit;
065
066 if (DEBUG) {
067 System.out.println("start: " + start + ", limit: " + limit);
068 for (int i = start; i < limit; i++) {
069 GlyphJustificationInfo gji = info[i];
070 System.out.println("w: " + gji.weight + ", gp: "
071 + gji.growPriority + ", gll: "
072 + gji.growLeftLimit + ", grl: "
073 + gji.growRightLimit);
074 }
075 }
076 }
077
078 public static final int MAX_PRIORITY = 3;
079
080 /**
081 * Return an array of deltas twice as long as the original info array,
082 * indicating the amount by which each side of each glyph should grow
083 * or shrink.
084 *
085 * Delta should be positive to expand the line, and negative to compress it.
086 */
087 public float[] justify(float delta) {
088 float[] deltas = new float[info.length * 2];
089
090 boolean grow = delta > 0;
091
092 if (DEBUG)
093 System.out.println("delta: " + delta);
094
095 // make separate passes through glyphs in order of decreasing priority
096 // until justifyDelta is zero or we run out of priorities.
097 int fallbackPriority = -1;
098 for (int p = 0; delta != 0; p++) {
099 /*
100 * special case 'fallback' iteration, set flag and recheck
101 * highest priority
102 */
103 boolean lastPass = p > MAX_PRIORITY;
104 if (lastPass)
105 p = fallbackPriority;
106
107 // pass through glyphs, first collecting weights and limits
108 float weight = 0;
109 float gslimit = 0;
110 float absorbweight = 0;
111 for (int i = start; i < limit; i++) {
112 GlyphJustificationInfo gi = info[i];
113 if ((grow ? gi.growPriority : gi.shrinkPriority) == p) {
114 if (fallbackPriority == -1) {
115 fallbackPriority = p;
116 }
117
118 if (i != start) { // ignore left of first character
119 weight += gi.weight;
120 if (grow) {
121 gslimit += gi.growLeftLimit;
122 if (gi.growAbsorb) {
123 absorbweight += gi.weight;
124 }
125 } else {
126 gslimit += gi.shrinkLeftLimit;
127 if (gi.shrinkAbsorb) {
128 absorbweight += gi.weight;
129 }
130 }
131 }
132
133 if (i + 1 != limit) { // ignore right of last character
134 weight += gi.weight;
135 if (grow) {
136 gslimit += gi.growRightLimit;
137 if (gi.growAbsorb) {
138 absorbweight += gi.weight;
139 }
140 } else {
141 gslimit += gi.shrinkRightLimit;
142 if (gi.shrinkAbsorb) {
143 absorbweight += gi.weight;
144 }
145 }
146 }
147 }
148 }
149
150 // did we hit the limit?
151 if (!grow) {
152 gslimit = -gslimit; // negative for negative deltas
153 }
154 boolean hitLimit = (weight == 0)
155 || (!lastPass && ((delta < 0) == (delta < gslimit)));
156 boolean absorbing = hitLimit && absorbweight > 0;
157
158 // predivide delta by weight
159 float weightedDelta = delta / weight; // not used if weight == 0
160
161 float weightedAbsorb = 0;
162 if (hitLimit && absorbweight > 0) {
163 weightedAbsorb = (delta - gslimit) / absorbweight;
164 }
165
166 if (DEBUG) {
167 System.out.println("pass: " + p + ", d: " + delta
168 + ", l: " + gslimit + ", w: " + weight
169 + ", aw: " + absorbweight + ", wd: "
170 + weightedDelta + ", wa: " + weightedAbsorb
171 + ", hit: " + (hitLimit ? "y" : "n"));
172 }
173
174 // now allocate this based on ratio of weight to total weight
175 int n = start * 2;
176 for (int i = start; i < limit; i++) {
177 GlyphJustificationInfo gi = info[i];
178 if ((grow ? gi.growPriority : gi.shrinkPriority) == p) {
179 if (i != start) { // ignore left
180 float d;
181 if (hitLimit) {
182 // factor in sign
183 d = grow ? gi.growLeftLimit
184 : -gi.shrinkLeftLimit;
185 if (absorbing) {
186 // sign factored in already
187 d += gi.weight * weightedAbsorb;
188 }
189 } else {
190 // sign factored in already
191 d = gi.weight * weightedDelta;
192 }
193
194 deltas[n] += d;
195 }
196 n++;
197
198 if (i + 1 != limit) { // ignore right
199 float d;
200 if (hitLimit) {
201 d = grow ? gi.growRightLimit
202 : -gi.shrinkRightLimit;
203 if (absorbing) {
204 d += gi.weight * weightedAbsorb;
205 }
206 } else {
207 d = gi.weight * weightedDelta;
208 }
209
210 deltas[n] += d;
211 }
212 n++;
213 } else {
214 n += 2;
215 }
216 }
217
218 if (!lastPass && hitLimit && !absorbing) {
219 delta -= gslimit;
220 } else {
221 delta = 0; // stop iteration
222 }
223 }
224
225 if (DEBUG) {
226 float total = 0;
227 for (int i = 0; i < deltas.length; i++) {
228 total += deltas[i];
229 System.out.print(deltas[i] + ", ");
230 if (i % 20 == 9) {
231 System.out.println();
232 }
233 }
234 System.out.println("\ntotal: " + total);
235 System.out.println();
236 }
237
238 return deltas;
239 }
240 }
|