J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 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 | |
| 41 | package 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 | |
| 50 | class 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 | } |