You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GlyphMappingTable.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.complexscripts.fonts;
  19. import java.util.Arrays;
  20. import java.util.Iterator;
  21. import java.util.List;
  22. // CSOFF: LineLengthCheck
  23. /**
  24. * <p>Base class implementation of glyph mapping table. This base
  25. * class maps glyph indices to arbitrary integers (mappping indices), and
  26. * is used to implement both glyph coverage and glyph class maps.</p>
  27. *
  28. * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p>
  29. */
  30. public class GlyphMappingTable {
  31. /** empty mapping table */
  32. public static final int GLYPH_MAPPING_TYPE_EMPTY = 0;
  33. /** mapped mapping table */
  34. public static final int GLYPH_MAPPING_TYPE_MAPPED = 1;
  35. /** range based mapping table */
  36. public static final int GLYPH_MAPPING_TYPE_RANGE = 2;
  37. /**
  38. * Obtain mapping type.
  39. * @return mapping format type
  40. */
  41. public int getType() {
  42. return -1;
  43. }
  44. /**
  45. * Obtain mapping entries.
  46. * @return list of mapping entries
  47. */
  48. public List getEntries() {
  49. return null;
  50. }
  51. /**
  52. * Obtain size of mapping table, i.e., ciMax + 1, where ciMax is the maximum
  53. * mapping index.
  54. * @return size of mapping table
  55. */
  56. public int getMappingSize() {
  57. return 0;
  58. }
  59. /**
  60. * Map glyph identifier (code) to coverge index. Returns -1 if glyph identifier is not in the domain of
  61. * the mapping table.
  62. * @param gid glyph identifier (code)
  63. * @return non-negative glyph mapping index or -1 if glyph identifiers is not mapped by table
  64. */
  65. public int getMappedIndex(int gid) {
  66. return -1;
  67. }
  68. /** empty mapping table base class */
  69. protected static class EmptyMappingTable extends GlyphMappingTable {
  70. /**
  71. * Construct empty mapping table.
  72. */
  73. public EmptyMappingTable() {
  74. this ((List) null);
  75. }
  76. /**
  77. * Construct empty mapping table with entries (ignored).
  78. * @param entries list of entries (ignored)
  79. */
  80. public EmptyMappingTable(List entries) {
  81. }
  82. /** {@inheritDoc} */
  83. public int getType() {
  84. return GLYPH_MAPPING_TYPE_EMPTY;
  85. }
  86. /** {@inheritDoc} */
  87. public List getEntries() {
  88. return new java.util.ArrayList();
  89. }
  90. /** {@inheritDoc} */
  91. public int getMappingSize() {
  92. return 0;
  93. }
  94. /** {@inheritDoc} */
  95. public int getMappedIndex(int gid) {
  96. return -1;
  97. }
  98. }
  99. /** mapped mapping table base class */
  100. protected static class MappedMappingTable extends GlyphMappingTable {
  101. /**
  102. * Construct mapped mapping table.
  103. */
  104. public MappedMappingTable() {
  105. }
  106. /** {@inheritDoc} */
  107. public int getType() {
  108. return GLYPH_MAPPING_TYPE_MAPPED;
  109. }
  110. }
  111. /** range mapping table base class */
  112. protected abstract static class RangeMappingTable extends GlyphMappingTable {
  113. private int[] sa; // array of range (inclusive) starts
  114. private int[] ea; // array of range (inclusive) ends
  115. private int[] ma; // array of range mapped values
  116. private int miMax = -1;
  117. /**
  118. * Construct range mapping table.
  119. * @param entries of mapping ranges
  120. */
  121. public RangeMappingTable(List entries) {
  122. populate(entries);
  123. }
  124. /** {@inheritDoc} */
  125. public int getType() {
  126. return GLYPH_MAPPING_TYPE_RANGE;
  127. }
  128. /** {@inheritDoc} */
  129. public List getEntries() {
  130. List entries = new java.util.ArrayList();
  131. if (sa != null) {
  132. for (int i = 0, n = sa.length; i < n; i++) {
  133. entries.add(new MappingRange(sa [ i ], ea [ i ], ma [ i ]));
  134. }
  135. }
  136. return entries;
  137. }
  138. /** {@inheritDoc} */
  139. public int getMappingSize() {
  140. return miMax + 1;
  141. }
  142. /** {@inheritDoc} */
  143. public int getMappedIndex(int gid) {
  144. int i;
  145. int mi;
  146. if ((i = Arrays.binarySearch(sa, gid)) >= 0) {
  147. mi = getMappedIndex(gid, sa [ i ], ma [ i ]); // matches start of (some) range
  148. } else if ((i = -(i + 1)) == 0) {
  149. mi = -1; // precedes first range
  150. } else if (gid > ea [ --i ]) {
  151. mi = -1; // follows preceding (or last) range
  152. } else {
  153. mi = getMappedIndex(gid, sa [ i ], ma [ i ]); // intersects (some) range
  154. }
  155. return mi;
  156. }
  157. /**
  158. * Map glyph identifier (code) to coverge index. Returns -1 if glyph identifier is not in the domain of
  159. * the mapping table.
  160. * @param gid glyph identifier (code)
  161. * @param s start of range
  162. * @param m mapping value
  163. * @return non-negative glyph mapping index or -1 if glyph identifiers is not mapped by table
  164. */
  165. public abstract int getMappedIndex(int gid, int s, int m);
  166. private void populate(List entries) {
  167. int i = 0;
  168. int n = entries.size();
  169. int gidMax = -1;
  170. int miMax = -1;
  171. int[] sa = new int [ n ];
  172. int[] ea = new int [ n ];
  173. int[] ma = new int [ n ];
  174. for (Iterator it = entries.iterator(); it.hasNext();) {
  175. Object o = it.next();
  176. if (o instanceof MappingRange) {
  177. MappingRange r = (MappingRange) o;
  178. int gs = r.getStart();
  179. int ge = r.getEnd();
  180. int mi = r.getIndex();
  181. if ((gs < 0) || (gs > 65535)) {
  182. throw new AdvancedTypographicTableFormatException("illegal glyph range: [" + gs + "," + ge + "]: bad start index");
  183. } else if ((ge < 0) || (ge > 65535)) {
  184. throw new AdvancedTypographicTableFormatException("illegal glyph range: [" + gs + "," + ge + "]: bad end index");
  185. } else if (gs > ge) {
  186. throw new AdvancedTypographicTableFormatException("illegal glyph range: [" + gs + "," + ge + "]: start index exceeds end index");
  187. } else if (gs < gidMax) {
  188. throw new AdvancedTypographicTableFormatException("out of order glyph range: [" + gs + "," + ge + "]");
  189. } else if (mi < 0) {
  190. throw new AdvancedTypographicTableFormatException("illegal mapping index: " + mi);
  191. } else {
  192. int miLast;
  193. sa [ i ] = gs;
  194. ea [ i ] = gidMax = ge;
  195. ma [ i ] = mi;
  196. if ((miLast = mi + (ge - gs)) > miMax) {
  197. miMax = miLast;
  198. }
  199. i++;
  200. }
  201. } else {
  202. throw new AdvancedTypographicTableFormatException("illegal mapping entry, must be Integer: " + o);
  203. }
  204. }
  205. assert i == n;
  206. assert this.sa == null;
  207. assert this.ea == null;
  208. assert this.ma == null;
  209. this.sa = sa;
  210. this.ea = ea;
  211. this.ma = ma;
  212. this.miMax = miMax;
  213. }
  214. /** {@inheritDoc} */
  215. public String toString() {
  216. StringBuffer sb = new StringBuffer();
  217. sb.append('{');
  218. for (int i = 0, n = sa.length; i < n; i++) {
  219. if (i > 0) {
  220. sb.append(',');
  221. }
  222. sb.append('[');
  223. sb.append(Integer.toString(sa [ i ]));
  224. sb.append(Integer.toString(ea [ i ]));
  225. sb.append("]:");
  226. sb.append(Integer.toString(ma [ i ]));
  227. }
  228. sb.append('}');
  229. return sb.toString();
  230. }
  231. }
  232. /**
  233. * The <code>MappingRange</code> class encapsulates a glyph [start,end] range and
  234. * a mapping index.
  235. */
  236. public static class MappingRange {
  237. private final int gidStart; // first glyph in range (inclusive)
  238. private final int gidEnd; // last glyph in range (inclusive)
  239. private final int index; // mapping index;
  240. /**
  241. * Instantiate a mapping range.
  242. */
  243. public MappingRange() {
  244. this (0, 0, 0);
  245. }
  246. /**
  247. * Instantiate a specific mapping range.
  248. * @param gidStart start of range
  249. * @param gidEnd end of range
  250. * @param index mapping index
  251. */
  252. public MappingRange(int gidStart, int gidEnd, int index) {
  253. if ((gidStart < 0) || (gidEnd < 0) || (index < 0)) {
  254. throw new AdvancedTypographicTableFormatException();
  255. } else if (gidStart > gidEnd) {
  256. throw new AdvancedTypographicTableFormatException();
  257. } else {
  258. this.gidStart = gidStart;
  259. this.gidEnd = gidEnd;
  260. this.index = index;
  261. }
  262. }
  263. /** @return start of range */
  264. public int getStart() {
  265. return gidStart;
  266. }
  267. /** @return end of range */
  268. public int getEnd() {
  269. return gidEnd;
  270. }
  271. /** @return mapping index */
  272. public int getIndex() {
  273. return index;
  274. }
  275. /** @return interval as a pair of integers */
  276. public int[] getInterval() {
  277. return new int[] { gidStart, gidEnd };
  278. }
  279. /**
  280. * Obtain interval, filled into first two elements of specified array, or returning new array.
  281. * @param interval an array of length two or greater or null
  282. * @return interval as a pair of integers, filled into specified array
  283. */
  284. public int[] getInterval(int[] interval) {
  285. if ((interval == null) || (interval.length != 2)) {
  286. throw new IllegalArgumentException();
  287. } else {
  288. interval[0] = gidStart;
  289. interval[1] = gidEnd;
  290. }
  291. return interval;
  292. }
  293. /** @return length of interval */
  294. public int getLength() {
  295. return gidStart - gidEnd;
  296. }
  297. }
  298. }