1. /*
  2. * @(#)TextJustifier.java 1.16 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. /*
  11. * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
  12. * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
  13. *
  14. * The original version of this source code and documentation is
  15. * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
  16. * of IBM. These materials are provided under terms of a License
  17. * Agreement between Taligent and Sun. This technology is protected
  18. * by multiple US and International patents.
  19. *
  20. * This notice and attribution to Taligent may not be removed.
  21. * Taligent is a registered trademark of Taligent, Inc.
  22. *
  23. */
  24. package java.awt.font;
  25. /*
  26. * one info for each side of each glyph
  27. * separate infos for grow and shrink case
  28. * !!! this doesn't really need to be a separate class. If we keep it
  29. * separate, probably the newJustify code from TextLayout belongs here as well.
  30. */
  31. class TextJustifier {
  32. private GlyphJustificationInfo[] info;
  33. private int start;
  34. private int limit;
  35. static boolean DEBUG = false;
  36. /**
  37. * Initialize the justifier with an array of infos corresponding to each
  38. * glyph. Start and limit indicate the range of the array to examine.
  39. */
  40. TextJustifier(GlyphJustificationInfo[] info, int start, int limit) {
  41. this.info = info;
  42. this.start = start;
  43. this.limit = limit;
  44. if (DEBUG) {
  45. System.out.println("start: " + start + ", limit: " + limit);
  46. for (int i = start; i < limit; i++) {
  47. GlyphJustificationInfo gji = info[i];
  48. System.out.println("w: " + gji.weight + ", gp: " +
  49. gji.growPriority + ", gll: " +
  50. gji.growLeftLimit + ", grl: " +
  51. gji.growRightLimit);
  52. }
  53. }
  54. }
  55. public static final int MAX_PRIORITY = 3;
  56. /**
  57. * Return an array of deltas twice as long as the original info array,
  58. * indicating the amount by which each side of each glyph should grow
  59. * or shrink.
  60. *
  61. * Delta should be positive to expand the line, and negative to compress it.
  62. */
  63. public float[] justify(float delta) {
  64. float[] deltas = new float[info.length * 2];
  65. boolean grow = delta > 0;
  66. if (DEBUG)
  67. System.out.println("delta: " + delta);
  68. // make separate passes through glyphs in order of decreasing priority
  69. // until justifyDelta is zero or we run out of priorities.
  70. int fallbackPriority = -1;
  71. for (int p = 0; delta != 0; p++) {
  72. /*
  73. * special case 'fallback' iteration, set flag and recheck
  74. * highest priority
  75. */
  76. boolean lastPass = p > MAX_PRIORITY;
  77. if (lastPass)
  78. p = fallbackPriority;
  79. // pass through glyphs, first collecting weights and limits
  80. float weight = 0;
  81. float gslimit = 0;
  82. float absorbweight = 0;
  83. for (int i = start; i < limit; i++) {
  84. GlyphJustificationInfo gi = info[i];
  85. if ((grow ? gi.growPriority : gi.shrinkPriority) == p) {
  86. if (fallbackPriority == -1) {
  87. fallbackPriority = p;
  88. }
  89. if (i != start) { // ignore left of first character
  90. weight += gi.weight;
  91. if (grow) {
  92. gslimit += gi.growLeftLimit;
  93. if (gi.growAbsorb) {
  94. absorbweight += gi.weight;
  95. }
  96. } else {
  97. gslimit += gi.shrinkLeftLimit;
  98. if (gi.shrinkAbsorb) {
  99. absorbweight += gi.weight;
  100. }
  101. }
  102. }
  103. if (i + 1 != limit) { // ignore right of last character
  104. weight += gi.weight;
  105. if (grow) {
  106. gslimit += gi.growRightLimit;
  107. if (gi.growAbsorb) {
  108. absorbweight += gi.weight;
  109. }
  110. } else {
  111. gslimit += gi.shrinkRightLimit;
  112. if (gi.shrinkAbsorb) {
  113. absorbweight += gi.weight;
  114. }
  115. }
  116. }
  117. }
  118. }
  119. // did we hit the limit?
  120. if (!grow) {
  121. gslimit = -gslimit; // negative for negative deltas
  122. }
  123. boolean hitLimit = (weight == 0) || (!lastPass && ((delta < 0) == (delta < gslimit)));
  124. boolean absorbing = hitLimit && absorbweight > 0;
  125. // predivide delta by weight
  126. float weightedDelta = delta / weight; // not used if weight == 0
  127. float weightedAbsorb = 0;
  128. if (hitLimit && absorbweight > 0) {
  129. weightedAbsorb = (delta - gslimit) / absorbweight;
  130. }
  131. if (DEBUG) {
  132. System.out.println("pass: " + p +
  133. ", d: " + delta +
  134. ", l: " + gslimit +
  135. ", w: " + weight +
  136. ", aw: " + absorbweight +
  137. ", wd: " + weightedDelta +
  138. ", wa: " + weightedAbsorb +
  139. ", hit: " + (hitLimit ? "y" : "n"));
  140. }
  141. // now allocate this based on ratio of weight to total weight
  142. int n = start * 2;
  143. for (int i = start; i < limit; i++) {
  144. GlyphJustificationInfo gi = info[i];
  145. if ((grow ? gi.growPriority : gi.shrinkPriority) == p) {
  146. if (i != start) { // ignore left
  147. float d;
  148. if (hitLimit) {
  149. // factor in sign
  150. d = grow ? gi.growLeftLimit : -gi.shrinkLeftLimit;
  151. if (absorbing) {
  152. // sign factored in already
  153. d += gi.weight * weightedAbsorb;
  154. }
  155. } else {
  156. // sign factored in already
  157. d = gi.weight * weightedDelta;
  158. }
  159. deltas[n] += d;
  160. }
  161. n++;
  162. if (i + 1 != limit) { // ignore right
  163. float d;
  164. if (hitLimit) {
  165. d = grow ? gi.growRightLimit : -gi.shrinkRightLimit;
  166. if (absorbing) {
  167. d += gi.weight * weightedAbsorb;
  168. }
  169. } else {
  170. d = gi.weight * weightedDelta;
  171. }
  172. deltas[n] += d;
  173. }
  174. n++;
  175. } else {
  176. n += 2;
  177. }
  178. }
  179. if (!lastPass && hitLimit && !absorbing) {
  180. delta -= gslimit;
  181. } else {
  182. delta = 0; // stop iteration
  183. }
  184. }
  185. if (DEBUG) {
  186. float total = 0;
  187. for (int i = 0; i < deltas.length; i++) {
  188. total += deltas[i];
  189. System.out.print(deltas[i] + ", ");
  190. if (i % 20 == 9) {
  191. System.out.println();
  192. }
  193. }
  194. System.out.println("\ntotal: " + total);
  195. System.out.println();
  196. }
  197. return deltas;
  198. }
  199. }