1. /*
  2. * @(#)CharBasedLigaturizer.java 1.3 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. /*
  8. * @(#)CharBasedLigaturizer.java 1.1 98/07/31
  9. *
  10. * (C) Copyright IBM Corp. 1998 - All Rights Reserved
  11. */
  12. package javax.swing.text;
  13. import sun.awt.font.Ligaturizer.Filter;
  14. class CharBasedLigaturizer extends Ligaturizer {
  15. private char[] data;
  16. private Filter filter;
  17. /**
  18. * Constructs a new ligaturizer using a ligature tree encoded as a character array.
  19. * Clients should not modify the data array.
  20. */
  21. CharBasedLigaturizer(char[] data) {
  22. this.data = data;
  23. this.filter = null;
  24. }
  25. /*
  26. * Constructs a new ligaturizer using a ligature tree encoded as a character array.
  27. * This applies the filter to restrict the ligatures encoded by this tree. Clients
  28. * should not modify the data array.
  29. */
  30. CharBasedLigaturizer(char[] data, Filter f) {
  31. char[] temp = (char[])data.clone();
  32. int len = recursiveRestrict(0, temp, f);
  33. if (2 * len < temp.length) {
  34. temp = compress(temp);
  35. }
  36. this.data = temp;
  37. this.filter = f;
  38. }
  39. /*
  40. * Return a ligaturizer that only generates ligatures accepted by
  41. * the provided filter.
  42. */
  43. Ligaturizer restrict(Filter f) {
  44. if (!f.equals(filter)) {
  45. return new CharBasedLigaturizer(data, f);
  46. }
  47. return this;
  48. }
  49. /**
  50. * Convert length chars starting at start in text to include ligatures.
  51. * Text is in visual order. The leftmost character that forms a
  52. * ligature is replaced by the character for the ligature. The remaining
  53. * characters that form the ligature are replaced by U+FFFF.
  54. *
  55. * If a ligature explicitly defines use of a combining mark, that mark
  56. * will be replaced. Otherwise, a ligature may still form 'around'
  57. * combining marks. The characters that form such a ligature will be
  58. * replaced, but the combining mark or marks remain unchanged and in
  59. * their original positions. This allows combining marks to be
  60. * placed intelligently on the ligature.
  61. */
  62. void ligaturize(char[] text, int start, int length) {
  63. char[] ligbuf = new char[50];
  64. // Note: attempt to rewrite to speed up non-lig to non-lig loop
  65. // *increased* time by 50%, must have confused the jit. There
  66. // must be a way to do this and not fool the jit, but perhaps
  67. // it's not worth finding out.
  68. int w = start;
  69. int limit = start + length;
  70. while (w < limit) {
  71. int r = w;
  72. int lig = 0; // root
  73. // int matchlig = -1;
  74. // int matchr = r + 1;
  75. int matchr = -1;
  76. // goback:
  77. do {
  78. char c = text[r];
  79. int nextlig = subnode(lig, c);
  80. if (nextlig != -1) {
  81. lig = nextlig;
  82. ligbuf[r-w] = '\uffff';
  83. c = data[lig + 1];
  84. if (c != '\uffff') {
  85. ligbuf[0] = c;
  86. // matchlig = lig;
  87. // matchr = r+1;
  88. matchr = r;
  89. }
  90. }
  91. // else if (matchlig == -1) {
  92. // ++w;
  93. // continue goback;
  94. // }
  95. else if (isCombiningMark(c)) {
  96. ligbuf[r-w] = c;
  97. } else {
  98. break;
  99. }
  100. } while (++r < limit);
  101. // if (matchlig != -1) {
  102. // System.arraycopy(ligbuf, 0, text, w, matchr - w);
  103. // }
  104. // w = matchr;
  105. if (matchr != -1) {
  106. System.arraycopy(ligbuf, 0, text, w, matchr - w + 1);
  107. w = matchr;
  108. }
  109. ++w;
  110. }
  111. }
  112. private static boolean isCombiningMark(char ch) {
  113. return ((((1 << Character.NON_SPACING_MARK) |
  114. (1 << Character.ENCLOSING_MARK) |
  115. (1 << Character.COMBINING_SPACING_MARK)) >> Character.getType(ch)) & 1) != 0;
  116. }
  117. // assume no char combines with more than 255 other chars
  118. // !!! we'll fail otherwise
  119. // should build pows value into table instead,
  120. // and use only for tables longer than a certain length
  121. private static final int[] exp2 = {
  122. 1, 2, 4, 8, 16, 32, 64, 128, 256
  123. };
  124. private static final int[] pows = new int[256];
  125. static {
  126. int pow = 0;
  127. for (int i = 1; i < pows.length; i++) {
  128. if (i >= exp2[pow]) {
  129. ++pow;
  130. }
  131. pows[i] = pow - 1;
  132. }
  133. }
  134. // fast binary search
  135. private int subnode(int pos, char c) {
  136. int inx;
  137. int l = data[pos + 2]; // length of subnode index
  138. if (l == 0) {
  139. return -1;
  140. }
  141. int p = pos + 3; // base of subnode index
  142. if (l == 1) {
  143. inx = p;
  144. } else {
  145. int pow = pows[l];
  146. int aux = l - exp2[pow];
  147. inx = exp2[pow] - 1 + p;
  148. if (c >= data[data[p + aux]]) {
  149. inx += aux;
  150. }
  151. switch (pow) {
  152. case 8: if (c < data[data[inx - 128]]) inx -= 128;
  153. case 7: if (c < data[data[inx - 64]]) inx -= 64;
  154. case 6: if (c < data[data[inx - 32]]) inx -= 32;
  155. case 5: if (c < data[data[inx - 16]]) inx-= 16;
  156. case 4: if (c < data[data[inx - 8]]) inx -= 8;
  157. case 3: if (c < data[data[inx - 4]]) inx -= 4;
  158. case 2: if (c < data[data[inx - 2]]) inx -= 2;
  159. case 1: if (c < data[data[inx - 1]]) inx -= 1;
  160. case 0: if (c < data[data[inx]]) inx -= 1;
  161. }
  162. }
  163. int n = data[inx];
  164. if (c == data[n]) {
  165. return n;
  166. }
  167. return -1;
  168. }
  169. /*
  170. // linear search
  171. private int oldsubnode(int pos, char c) {
  172. int p = pos + 3;
  173. int e = p + data[pos + 2];
  174. while (p < e) {
  175. int n = data[p];
  176. char t = data[n];
  177. if (t == c) {
  178. return n;
  179. }
  180. if (t > c) {
  181. break;
  182. }
  183. ++p;
  184. }
  185. return -1;
  186. }
  187. */
  188. /*
  189. * Apply filter to data in newData starting from node at pos, modifying
  190. * data and returning the count of the data elements used by the node at pos
  191. * and the nodes in its subtree.
  192. *
  193. * If the filter does not accept the ligature at this node, replace the
  194. * ligature value with u+ffff. Recursively call over subtrees to gather
  195. * count of subtrees leading to a valid ligature, and their total length.
  196. * Reset the count, and rewrite the first count elements in the subnode
  197. * list to hold the positions of these subnodes.
  198. */
  199. private static int recursiveRestrict(int pos, char[] newData, Filter f) {
  200. int len = 0;
  201. int ncount = 0;
  202. int count = newData[pos + 2];
  203. for (int i = 0; i < count; ++i) {
  204. int subpos = newData[pos + 3 + i];
  205. int sublen = recursiveRestrict(subpos, newData, f);
  206. if (sublen != 0) {
  207. newData[pos + 3 + ncount] = (char)subpos;
  208. ++ncount;
  209. len += sublen; // add size of subnode
  210. }
  211. }
  212. len += ncount; // add size of subnode list
  213. newData[pos + 2] = (char)ncount; // set new length of subnode list
  214. char lig = newData[pos + 1]; // this ligature
  215. if (lig != '\uffff') {
  216. if (f.accepts(lig)) {
  217. len += 3; // need space for this node if use this ligature
  218. }
  219. else {
  220. newData[pos + 1] = '\uffff'; // remove this ligature
  221. if (ncount > 0) {
  222. len += 3; // even if no ligature here, need space for this node if subtree is valid
  223. }
  224. }
  225. }
  226. return len;
  227. }
  228. /**
  229. * Compress the data in the array to remove space used by nodes that have
  230. * been filtered out. Return the new array.
  231. */
  232. private static char[] compress(char[] data) {
  233. int len = recursiveCompress(data, 0, 0);
  234. char[] newData = new char[len];
  235. System.arraycopy(data, 0, newData, 0, len);
  236. return newData;
  237. }
  238. private static int recursiveCompress(char[] data, int pos, int w) {
  239. data[w] = data[pos];
  240. data[++w] = data[++pos];
  241. int count = data[++pos];
  242. data[++w] = (char)count;
  243. int base = w;
  244. w += count + 1;
  245. while (--count >= 0) {
  246. int oldpos = data[++pos];
  247. data[++base] = (char)w;
  248. w = recursiveCompress(data, oldpos, w);
  249. }
  250. return w;
  251. }
  252. public String toString() {
  253. StringBuffer buf = new StringBuffer(super.toString());
  254. buf.append("[filter: ");
  255. buf.append(filter);
  256. // buf.append(", data: ");
  257. // LigatureDataBuilder.dumpUC(new String(data), buf);
  258. buf.append("]");
  259. return buf.toString();
  260. }
  261. }