blob: 665a09511b001bfb2c595cecacd46b06b8625c3b [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.awt.font;
42
43/*
44 * one info for each side of each glyph
45 * separate infos for grow and shrink case
46 * !!! this doesn't really need to be a separate class. If we keep it
47 * separate, probably the newJustify code from TextLayout belongs here as well.
48 */
49
50class TextJustifier {
51 private GlyphJustificationInfo[] info;
52 private int start;
53 private int limit;
54
55 static boolean DEBUG = false;
56
57 /**
58 * Initialize the justifier with an array of infos corresponding to each
59 * glyph. Start and limit indicate the range of the array to examine.
60 */
61 TextJustifier(GlyphJustificationInfo[] info, int start, int limit) {
62 this.info = info;
63 this.start = start;
64 this.limit = limit;
65
66 if (DEBUG) {
67 System.out.println("start: " + start + ", limit: " + limit);
68 for (int i = start; i < limit; i++) {
69 GlyphJustificationInfo gji = info[i];
70 System.out.println("w: " + gji.weight + ", gp: " +
71 gji.growPriority + ", gll: " +
72 gji.growLeftLimit + ", grl: " +
73 gji.growRightLimit);
74 }
75 }
76 }
77
78 public static final int MAX_PRIORITY = 3;
79
80 /**
81 * Return an array of deltas twice as long as the original info array,
82 * indicating the amount by which each side of each glyph should grow
83 * or shrink.
84 *
85 * Delta should be positive to expand the line, and negative to compress it.
86 */
87 public float[] justify(float delta) {
88 float[] deltas = new float[info.length * 2];
89
90 boolean grow = delta > 0;
91
92 if (DEBUG)
93 System.out.println("delta: " + delta);
94
95 // make separate passes through glyphs in order of decreasing priority
96 // until justifyDelta is zero or we run out of priorities.
97 int fallbackPriority = -1;
98 for (int p = 0; delta != 0; p++) {
99 /*
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) || (!lastPass && ((delta < 0) == (delta < gslimit)));
155 boolean absorbing = hitLimit && absorbweight > 0;
156
157 // predivide delta by weight
158 float weightedDelta = delta / weight; // not used if weight == 0
159
160 float weightedAbsorb = 0;
161 if (hitLimit && absorbweight > 0) {
162 weightedAbsorb = (delta - gslimit) / absorbweight;
163 }
164
165 if (DEBUG) {
166 System.out.println("pass: " + p +
167 ", d: " + delta +
168 ", l: " + gslimit +
169 ", w: " + weight +
170 ", aw: " + absorbweight +
171 ", wd: " + weightedDelta +
172 ", wa: " + weightedAbsorb +
173 ", hit: " + (hitLimit ? "y" : "n"));
174 }
175
176 // now allocate this based on ratio of weight to total weight
177 int n = start * 2;
178 for (int i = start; i < limit; i++) {
179 GlyphJustificationInfo gi = info[i];
180 if ((grow ? gi.growPriority : gi.shrinkPriority) == p) {
181 if (i != start) { // ignore left
182 float d;
183 if (hitLimit) {
184 // factor in sign
185 d = grow ? gi.growLeftLimit : -gi.shrinkLeftLimit;
186 if (absorbing) {
187 // sign factored in already
188 d += gi.weight * weightedAbsorb;
189 }
190 } else {
191 // sign factored in already
192 d = gi.weight * weightedDelta;
193 }
194
195 deltas[n] += d;
196 }
197 n++;
198
199 if (i + 1 != limit) { // ignore right
200 float d;
201 if (hitLimit) {
202 d = grow ? gi.growRightLimit : -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}