Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

GlyphProcessingState.java 47KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235
  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.nio.Buffer;
  20. import java.nio.IntBuffer;
  21. import java.util.ArrayList;
  22. import java.util.List;
  23. import org.apache.fop.complexscripts.util.CharAssociation;
  24. import org.apache.fop.complexscripts.util.GlyphContextTester;
  25. import org.apache.fop.complexscripts.util.GlyphSequence;
  26. import org.apache.fop.complexscripts.util.GlyphTester;
  27. import org.apache.fop.complexscripts.util.ScriptContextTester;
  28. // CSOFF: LineLengthCheck
  29. /**
  30. * <p>The <code>GlyphProcessingState</code> implements a common, base state object used during glyph substitution
  31. * and positioning processing.</p>
  32. *
  33. * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p>
  34. */
  35. public class GlyphProcessingState {
  36. /** governing glyph definition table */
  37. protected GlyphDefinitionTable gdef;
  38. /** governing script */
  39. protected String script;
  40. /** governing language */
  41. protected String language;
  42. /** governing feature */
  43. protected String feature;
  44. /** current input glyph sequence */
  45. protected GlyphSequence igs;
  46. /** current index in input sequence */
  47. protected int index;
  48. /** last (maximum) index of input sequence (exclusive) */
  49. protected int indexLast;
  50. /** consumed, updated after each successful subtable application */
  51. protected int consumed;
  52. /** lookup flags */
  53. protected int lookupFlags;
  54. /** class match set */
  55. protected int classMatchSet;
  56. /** script specific context tester or null */
  57. protected ScriptContextTester sct;
  58. /** glyph context tester or null */
  59. protected GlyphContextTester gct;
  60. /** ignore base glyph tester */
  61. protected GlyphTester ignoreBase;
  62. /** ignore ligature glyph tester */
  63. protected GlyphTester ignoreLigature;
  64. /** ignore mark glyph tester */
  65. protected GlyphTester ignoreMark;
  66. /** default ignore glyph tester */
  67. protected GlyphTester ignoreDefault;
  68. /** current subtable */
  69. private GlyphSubtable subtable;
  70. /**
  71. * Construct default (reset) glyph processing state.
  72. */
  73. public GlyphProcessingState() {
  74. }
  75. /**
  76. * Construct glyph processing state.
  77. * @param gs input glyph sequence
  78. * @param script script identifier
  79. * @param language language identifier
  80. * @param feature feature identifier
  81. * @param sct script context tester (or null)
  82. */
  83. protected GlyphProcessingState(GlyphSequence gs, String script, String language, String feature, ScriptContextTester sct) {
  84. this.script = script;
  85. this.language = language;
  86. this.feature = feature;
  87. this.igs = gs;
  88. this.indexLast = gs.getGlyphCount();
  89. this.sct = sct;
  90. this.gct = (sct != null) ? sct.getTester(feature) : null;
  91. this.ignoreBase = new GlyphTester() {
  92. public boolean test(int gi, int flags) {
  93. return isIgnoredBase(gi, flags);
  94. }
  95. };
  96. this.ignoreLigature = new GlyphTester() {
  97. public boolean test(int gi, int flags) {
  98. return isIgnoredLigature(gi, flags);
  99. }
  100. };
  101. this.ignoreMark = new GlyphTester() {
  102. public boolean test(int gi, int flags) {
  103. return isIgnoredMark(gi, flags);
  104. }
  105. };
  106. }
  107. /**
  108. * Construct glyph processing state using an existing state object using shallow copy
  109. * except as follows: input glyph sequence is copied deep except for its characters array.
  110. * @param s existing processing state to copy from
  111. */
  112. protected GlyphProcessingState(GlyphProcessingState s) {
  113. this (new GlyphSequence(s.igs), s.script, s.language, s.feature, s.sct);
  114. setPosition(s.index);
  115. }
  116. /**
  117. * Reset glyph processing state.
  118. * @param gs input glyph sequence
  119. * @param script script identifier
  120. * @param language language identifier
  121. * @param feature feature identifier
  122. * @param sct script context tester (or null)
  123. * @return this instance
  124. */
  125. protected GlyphProcessingState reset(GlyphSequence gs, String script, String language, String feature, ScriptContextTester sct) {
  126. this.gdef = null;
  127. this.script = script;
  128. this.language = language;
  129. this.feature = feature;
  130. this.igs = gs;
  131. this.index = 0;
  132. this.indexLast = gs.getGlyphCount();
  133. this.consumed = 0;
  134. this.lookupFlags = 0;
  135. this.classMatchSet = 0; // @SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
  136. this.sct = sct;
  137. this.gct = (sct != null) ? sct.getTester(feature) : null;
  138. this.ignoreBase = new GlyphTester() {
  139. public boolean test(int gi, int flags) {
  140. return isIgnoredBase(gi, flags);
  141. }
  142. };
  143. this.ignoreLigature = new GlyphTester() {
  144. public boolean test(int gi, int flags) {
  145. return isIgnoredLigature(gi, flags);
  146. }
  147. };
  148. this.ignoreMark = new GlyphTester() {
  149. public boolean test(int gi, int flags) {
  150. return isIgnoredMark(gi, flags);
  151. }
  152. };
  153. this.ignoreDefault = null;
  154. this.subtable = null;
  155. return this;
  156. }
  157. /**
  158. * Set governing glyph definition table.
  159. * @param gdef glyph definition table (or null, to unset)
  160. */
  161. public void setGDEF(GlyphDefinitionTable gdef) {
  162. if (this.gdef == null) {
  163. this.gdef = gdef;
  164. } else if (gdef == null) {
  165. this.gdef = null;
  166. }
  167. }
  168. /**
  169. * Obtain governing glyph definition table.
  170. * @return glyph definition table (or null, to not set)
  171. */
  172. public GlyphDefinitionTable getGDEF() {
  173. return gdef;
  174. }
  175. /**
  176. * Set governing lookup flags
  177. * @param flags lookup flags (or zero, to unset)
  178. */
  179. public void setLookupFlags(int flags) {
  180. if (this.lookupFlags == 0) {
  181. this.lookupFlags = flags;
  182. } else if (flags == 0) {
  183. this.lookupFlags = 0;
  184. }
  185. }
  186. /**
  187. * Obtain governing lookup flags.
  188. * @return lookup flags (zero may indicate unset or no flags)
  189. */
  190. public int getLookupFlags() {
  191. return lookupFlags;
  192. }
  193. /**
  194. * Obtain governing class match set.
  195. * @param gi glyph index that may be used to determine which match set applies
  196. * @return class match set (zero may indicate unset or no set)
  197. */
  198. public int getClassMatchSet(int gi) {
  199. return 0;
  200. }
  201. /**
  202. * Set default ignore tester.
  203. * @param ignoreDefault glyph tester (or null, to unset)
  204. */
  205. public void setIgnoreDefault(GlyphTester ignoreDefault) {
  206. if (this.ignoreDefault == null) {
  207. this.ignoreDefault = ignoreDefault;
  208. } else if (ignoreDefault == null) {
  209. this.ignoreDefault = null;
  210. }
  211. }
  212. /**
  213. * Obtain governing default ignores tester.
  214. * @return default ignores tester
  215. */
  216. public GlyphTester getIgnoreDefault() {
  217. return ignoreDefault;
  218. }
  219. /**
  220. * Update glyph subtable specific state. Each time a
  221. * different glyph subtable is to be applied, it is used
  222. * to update this state prior to application, after which
  223. * this state is to be reset.
  224. * @param st glyph subtable to use for update
  225. */
  226. public void updateSubtableState(GlyphSubtable st) {
  227. if (this.subtable != st) {
  228. setGDEF(st.getGDEF());
  229. setLookupFlags(st.getFlags());
  230. setIgnoreDefault(getIgnoreTester(getLookupFlags()));
  231. this.subtable = st;
  232. }
  233. }
  234. /**
  235. * Obtain current position index in input glyph sequence.
  236. * @return current index
  237. */
  238. public int getPosition() {
  239. return index;
  240. }
  241. /**
  242. * Set (seek to) position index in input glyph sequence.
  243. * @param index to seek to
  244. * @throws IndexOutOfBoundsException if index is less than zero
  245. * or exceeds last valid position
  246. */
  247. public void setPosition(int index) throws IndexOutOfBoundsException {
  248. if ((index >= 0) && (index <= indexLast)) {
  249. this.index = index;
  250. } else {
  251. throw new IndexOutOfBoundsException();
  252. }
  253. }
  254. /**
  255. * Obtain last valid position index in input glyph sequence.
  256. * @return current last index
  257. */
  258. public int getLastPosition() {
  259. return indexLast;
  260. }
  261. /**
  262. * Determine if at least one glyph remains in
  263. * input sequence.
  264. * @return true if one or more glyph remains
  265. */
  266. public boolean hasNext() {
  267. return hasNext(1);
  268. }
  269. /**
  270. * Determine if at least <code>count</code> glyphs remain in
  271. * input sequence.
  272. * @param count of glyphs to test
  273. * @return true if at least <code>count</code> glyphs are available
  274. */
  275. public boolean hasNext(int count) {
  276. return (index + count) <= indexLast;
  277. }
  278. /**
  279. * Update the current position index based upon previously consumed
  280. * glyphs, i.e., add the consuemd count to the current position index.
  281. * If no glyphs were previously consumed, then forces exactly one
  282. * glyph to be consumed.
  283. * @return the new (updated) position index
  284. */
  285. public int next() {
  286. if (index < indexLast) {
  287. // force consumption of at least one input glyph
  288. if (consumed == 0) {
  289. consumed = 1;
  290. }
  291. index += consumed;
  292. consumed = 0;
  293. if (index > indexLast) {
  294. index = indexLast;
  295. }
  296. }
  297. return index;
  298. }
  299. /**
  300. * Determine if at least one backtrack (previous) glyph is present
  301. * in input sequence.
  302. * @return true if one or more glyph remains
  303. */
  304. public boolean hasPrev() {
  305. return hasPrev(1);
  306. }
  307. /**
  308. * Determine if at least <code>count</code> backtrack (previous) glyphs
  309. * are present in input sequence.
  310. * @param count of glyphs to test
  311. * @return true if at least <code>count</code> glyphs are available
  312. */
  313. public boolean hasPrev(int count) {
  314. return (index - count) >= 0;
  315. }
  316. /**
  317. * Update the current position index based upon previously consumed
  318. * glyphs, i.e., subtract the consuemd count from the current position index.
  319. * If no glyphs were previously consumed, then forces exactly one
  320. * glyph to be consumed. This method is used to traverse an input
  321. * glyph sequence in reverse order.
  322. * @return the new (updated) position index
  323. */
  324. public int prev() {
  325. if (index > 0) {
  326. // force consumption of at least one input glyph
  327. if (consumed == 0) {
  328. consumed = 1;
  329. }
  330. index -= consumed;
  331. consumed = 0;
  332. if (index < 0) {
  333. index = 0;
  334. }
  335. }
  336. return index;
  337. }
  338. /**
  339. * Record the consumption of <code>count</code> glyphs such that
  340. * this consumption never exceeds the number of glyphs in the input glyph
  341. * sequence.
  342. * @param count of glyphs to consume
  343. * @return newly adjusted consumption count
  344. * @throws IndexOutOfBoundsException if count would cause consumption
  345. * to exceed count of glyphs in input glyph sequence
  346. */
  347. public int consume(int count) throws IndexOutOfBoundsException {
  348. if ((consumed + count) <= indexLast) {
  349. consumed += count;
  350. return consumed;
  351. } else {
  352. throw new IndexOutOfBoundsException();
  353. }
  354. }
  355. /**
  356. * Determine if any consumption has occurred.
  357. * @return true if consumption count is greater than zero
  358. */
  359. public boolean didConsume() {
  360. return consumed > 0;
  361. }
  362. /**
  363. * Obtain reference to input glyph sequence, which must not be modified.
  364. * @return input glyph sequence
  365. */
  366. public GlyphSequence getInput() {
  367. return igs;
  368. }
  369. /**
  370. * Obtain glyph at specified offset from current position.
  371. * @param offset from current position
  372. * @return glyph at specified offset from current position
  373. * @throws IndexOutOfBoundsException if no glyph available at offset
  374. */
  375. public int getGlyph(int offset) throws IndexOutOfBoundsException {
  376. int i = index + offset;
  377. if ((i >= 0) && (i < indexLast)) {
  378. return igs.getGlyph(i);
  379. } else {
  380. throw new IndexOutOfBoundsException("attempting index at " + i);
  381. }
  382. }
  383. public int getUnprocessedGlyph(int offset) throws IndexOutOfBoundsException {
  384. int i = index + offset;
  385. if ((i >= 0) && (i < indexLast)) {
  386. return igs.getUnprocessedGlyph(i);
  387. } else {
  388. throw new IndexOutOfBoundsException("Attempting to process glyph at index " + i);
  389. }
  390. }
  391. /**
  392. * Obtain glyph at current position.
  393. * @return glyph at current position
  394. * @throws IndexOutOfBoundsException if no glyph available
  395. */
  396. public int getGlyph() throws IndexOutOfBoundsException {
  397. return getGlyph(0);
  398. }
  399. /**
  400. * Set (replace) glyph at specified offset from current position.
  401. * @param offset from current position
  402. * @param glyph to set at specified offset from current position
  403. * @throws IndexOutOfBoundsException if specified offset is not valid position
  404. */
  405. public void setGlyph(int offset, int glyph) throws IndexOutOfBoundsException {
  406. int i = index + offset;
  407. if ((i >= 0) && (i < indexLast)) {
  408. igs.setGlyph(i, glyph);
  409. } else {
  410. throw new IndexOutOfBoundsException("attempting index at " + i);
  411. }
  412. }
  413. /**
  414. * Obtain character association of glyph at specified offset from current position.
  415. * @param offset from current position
  416. * @return character association of glyph at current position
  417. * @throws IndexOutOfBoundsException if offset results in an invalid index into input glyph sequence
  418. */
  419. public CharAssociation getAssociation(int offset) throws IndexOutOfBoundsException {
  420. int i = index + offset;
  421. if ((i >= 0) && (i < indexLast)) {
  422. return igs.getAssociation(i);
  423. } else {
  424. throw new IndexOutOfBoundsException("attempting index at " + i);
  425. }
  426. }
  427. /**
  428. * Obtain character association of glyph at current position.
  429. * @return character association of glyph at current position
  430. * @throws IndexOutOfBoundsException if no glyph available
  431. */
  432. public CharAssociation getAssociation() throws IndexOutOfBoundsException {
  433. return getAssociation(0);
  434. }
  435. /**
  436. * Obtain <code>count</code> glyphs starting at specified offset from current position. If
  437. * <code>reverseOrder</code> is true, then glyphs are returned in reverse order starting at specified offset
  438. * and going in reverse towards beginning of input glyph sequence.
  439. * @param offset from current position
  440. * @param count number of glyphs to obtain
  441. * @param reverseOrder true if to obtain in reverse order
  442. * @param ignoreTester glyph tester to use to determine which glyphs are ignored (or null, in which case none are ignored)
  443. * @param glyphs array to use to fetch glyphs
  444. * @param counts int[2] array to receive fetched glyph counts, where counts[0] will
  445. * receive the number of glyphs obtained, and counts[1] will receive the number of glyphs
  446. * ignored
  447. * @return array of glyphs
  448. * @throws IndexOutOfBoundsException if offset or count results in an
  449. * invalid index into input glyph sequence
  450. */
  451. public int[] getGlyphs(int offset, int count, boolean reverseOrder, GlyphTester ignoreTester, int[] glyphs, int[] counts) throws IndexOutOfBoundsException {
  452. if (count < 0) {
  453. count = getGlyphsAvailable(offset, reverseOrder, ignoreTester) [ 0 ];
  454. }
  455. int start = index + offset;
  456. if (start < 0) {
  457. throw new IndexOutOfBoundsException("will attempt index at " + start);
  458. } else if (!reverseOrder && ((start + count) > indexLast)) {
  459. throw new IndexOutOfBoundsException("will attempt index at " + (start + count));
  460. } else if (reverseOrder && ((start + 1) < count)) {
  461. throw new IndexOutOfBoundsException("will attempt index at " + (start - count));
  462. }
  463. if (glyphs == null) {
  464. glyphs = new int [ count ];
  465. } else if (glyphs.length != count) {
  466. throw new IllegalArgumentException("glyphs array is non-null, but its length (" + glyphs.length + "), is not equal to count (" + count + ")");
  467. }
  468. if (!reverseOrder) {
  469. return getGlyphsForward(start, count, ignoreTester, glyphs, counts);
  470. } else {
  471. return getGlyphsReverse(start, count, ignoreTester, glyphs, counts);
  472. }
  473. }
  474. private int[] getGlyphsForward(int start, int count, GlyphTester ignoreTester, int[] glyphs, int[] counts) throws IndexOutOfBoundsException {
  475. int counted = 0;
  476. int ignored = 0;
  477. for (int i = start, n = indexLast; (i < n) && (counted < count); i++) {
  478. int gi = getGlyph(i - index);
  479. if (gi == 65535) {
  480. ignored++;
  481. } else {
  482. if ((ignoreTester == null) || !ignoreTester.test(gi, getLookupFlags())) {
  483. glyphs [ counted++ ] = gi;
  484. } else {
  485. ignored++;
  486. }
  487. }
  488. }
  489. if ((counts != null) && (counts.length > 1)) {
  490. counts[0] = counted;
  491. counts[1] = ignored;
  492. }
  493. return glyphs;
  494. }
  495. private int[] getGlyphsReverse(int start, int count, GlyphTester ignoreTester, int[] glyphs, int[] counts) throws IndexOutOfBoundsException {
  496. int counted = 0;
  497. int ignored = 0;
  498. for (int i = start; (i >= 0) && (counted < count); i--) {
  499. int gi = getGlyph(i - index);
  500. if (gi == 65535) {
  501. ignored++;
  502. } else {
  503. if ((ignoreTester == null) || !ignoreTester.test(gi, getLookupFlags())) {
  504. glyphs [ counted++ ] = gi;
  505. } else {
  506. ignored++;
  507. }
  508. }
  509. }
  510. if ((counts != null) && (counts.length > 1)) {
  511. counts[0] = counted;
  512. counts[1] = ignored;
  513. }
  514. return glyphs;
  515. }
  516. /**
  517. * Obtain <code>count</code> glyphs starting at specified offset from current position. If
  518. * offset is negative, then glyphs are returned in reverse order starting at specified offset
  519. * and going in reverse towards beginning of input glyph sequence.
  520. * @param offset from current position
  521. * @param count number of glyphs to obtain
  522. * @param glyphs array to use to fetch glyphs
  523. * @param counts int[2] array to receive fetched glyph counts, where counts[0] will
  524. * receive the number of glyphs obtained, and counts[1] will receive the number of glyphs
  525. * ignored
  526. * @return array of glyphs
  527. * @throws IndexOutOfBoundsException if offset or count results in an
  528. * invalid index into input glyph sequence
  529. */
  530. public int[] getGlyphs(int offset, int count, int[] glyphs, int[] counts) throws IndexOutOfBoundsException {
  531. return getGlyphs(offset, count, offset < 0, ignoreDefault, glyphs, counts);
  532. }
  533. /**
  534. * Obtain all glyphs starting from current position to end of input glyph sequence.
  535. * @return array of available glyphs
  536. * @throws IndexOutOfBoundsException if no glyph available
  537. */
  538. public int[] getGlyphs() throws IndexOutOfBoundsException {
  539. return getGlyphs(0, indexLast - index, false, null, null, null);
  540. }
  541. /**
  542. * Obtain <code>count</code> ignored glyphs starting at specified offset from current position. If
  543. * <code>reverseOrder</code> is true, then glyphs are returned in reverse order starting at specified offset
  544. * and going in reverse towards beginning of input glyph sequence.
  545. * @param offset from current position
  546. * @param count number of glyphs to obtain
  547. * @param reverseOrder true if to obtain in reverse order
  548. * @param ignoreTester glyph tester to use to determine which glyphs are ignored (or null, in which case none are ignored)
  549. * @param glyphs array to use to fetch glyphs
  550. * @param counts int[2] array to receive fetched glyph counts, where counts[0] will
  551. * receive the number of glyphs obtained, and counts[1] will receive the number of glyphs
  552. * ignored
  553. * @return array of glyphs
  554. * @throws IndexOutOfBoundsException if offset or count results in an
  555. * invalid index into input glyph sequence
  556. */
  557. public int[] getIgnoredGlyphs(int offset, int count, boolean reverseOrder, GlyphTester ignoreTester, int[] glyphs, int[] counts) throws IndexOutOfBoundsException {
  558. return getGlyphs(offset, count, reverseOrder, new NotGlyphTester(ignoreTester), glyphs, counts);
  559. }
  560. /**
  561. * Obtain <code>count</code> ignored glyphs starting at specified offset from current position. If <code>offset</code> is
  562. * negative, then fetch in reverse order.
  563. * @param offset from current position
  564. * @param count number of glyphs to obtain
  565. * @return array of glyphs
  566. * @throws IndexOutOfBoundsException if offset or count results in an
  567. * invalid index into input glyph sequence
  568. */
  569. public int[] getIgnoredGlyphs(int offset, int count) throws IndexOutOfBoundsException {
  570. return getIgnoredGlyphs(offset, count, offset < 0, ignoreDefault, null, null);
  571. }
  572. /**
  573. * Determine if glyph at specified offset from current position is ignored. If <code>offset</code> is
  574. * negative, then test in reverse order.
  575. * @param offset from current position
  576. * @param ignoreTester glyph tester to use to determine which glyphs are ignored (or null, in which case none are ignored)
  577. * @return true if glyph is ignored
  578. * @throws IndexOutOfBoundsException if offset results in an
  579. * invalid index into input glyph sequence
  580. */
  581. public boolean isIgnoredGlyph(int offset, GlyphTester ignoreTester) throws IndexOutOfBoundsException {
  582. return (ignoreTester != null) && ignoreTester.test(getGlyph(offset), getLookupFlags());
  583. }
  584. /**
  585. * Determine if glyph at specified offset from current position is ignored. If <code>offset</code> is
  586. * negative, then test in reverse order.
  587. * @param offset from current position
  588. * @return true if glyph is ignored
  589. * @throws IndexOutOfBoundsException if offset results in an
  590. * invalid index into input glyph sequence
  591. */
  592. public boolean isIgnoredGlyph(int offset) throws IndexOutOfBoundsException {
  593. return isIgnoredGlyph(offset, ignoreDefault);
  594. }
  595. /**
  596. * Determine if glyph at current position is ignored.
  597. * @return true if glyph is ignored
  598. * @throws IndexOutOfBoundsException if offset results in an
  599. * invalid index into input glyph sequence
  600. */
  601. public boolean isIgnoredGlyph() throws IndexOutOfBoundsException {
  602. return isIgnoredGlyph(getPosition());
  603. }
  604. /**
  605. * Determine number of glyphs available starting at specified offset from current position. If
  606. * <code>reverseOrder</code> is true, then search backwards in input glyph sequence.
  607. * @param offset from current position
  608. * @param reverseOrder true if to obtain in reverse order
  609. * @param ignoreTester glyph tester to use to determine which glyphs to count (or null, in which case none are ignored)
  610. * @return an int[2] array where counts[0] is the number of glyphs available, and counts[1] is the number of glyphs ignored
  611. * @throws IndexOutOfBoundsException if offset or count results in an
  612. * invalid index into input glyph sequence
  613. */
  614. public int[] getGlyphsAvailable(int offset, boolean reverseOrder, GlyphTester ignoreTester) throws IndexOutOfBoundsException {
  615. int start = index + offset;
  616. if ((start < 0) || (start > indexLast)) {
  617. return new int[] { 0, 0 };
  618. } else if (!reverseOrder) {
  619. return getGlyphsAvailableForward(start, ignoreTester);
  620. } else {
  621. return getGlyphsAvailableReverse(start, ignoreTester);
  622. }
  623. }
  624. private int[] getGlyphsAvailableForward(int start, GlyphTester ignoreTester) throws IndexOutOfBoundsException {
  625. int counted = 0;
  626. int ignored = 0;
  627. if (ignoreTester == null) {
  628. counted = indexLast - start;
  629. } else {
  630. for (int i = start, n = indexLast; i < n; i++) {
  631. int gi = getGlyph(i - index);
  632. if (gi == 65535) {
  633. ignored++;
  634. } else {
  635. if (ignoreTester.test(gi, getLookupFlags())) {
  636. ignored++;
  637. } else {
  638. counted++;
  639. }
  640. }
  641. }
  642. }
  643. return new int[] { counted, ignored };
  644. }
  645. private int[] getGlyphsAvailableReverse(int start, GlyphTester ignoreTester) throws IndexOutOfBoundsException {
  646. int counted = 0;
  647. int ignored = 0;
  648. if (ignoreTester == null) {
  649. counted = start + 1;
  650. } else {
  651. for (int i = start; i >= 0; i--) {
  652. int gi = getGlyph(i - index);
  653. if (gi == 65535) {
  654. ignored++;
  655. } else {
  656. if (ignoreTester.test(gi, getLookupFlags())) {
  657. ignored++;
  658. } else {
  659. counted++;
  660. }
  661. }
  662. }
  663. }
  664. return new int[] { counted, ignored };
  665. }
  666. /**
  667. * Determine number of glyphs available starting at specified offset from current position. If
  668. * <code>reverseOrder</code> is true, then search backwards in input glyph sequence. Uses the
  669. * default ignores tester.
  670. * @param offset from current position
  671. * @param reverseOrder true if to obtain in reverse order
  672. * @return an int[2] array where counts[0] is the number of glyphs available, and counts[1] is the number of glyphs ignored
  673. * @throws IndexOutOfBoundsException if offset or count results in an
  674. * invalid index into input glyph sequence
  675. */
  676. public int[] getGlyphsAvailable(int offset, boolean reverseOrder) throws IndexOutOfBoundsException {
  677. return getGlyphsAvailable(offset, reverseOrder, ignoreDefault);
  678. }
  679. /**
  680. * Determine number of glyphs available starting at specified offset from current position. If
  681. * offset is negative, then search backwards in input glyph sequence. Uses the
  682. * default ignores tester.
  683. * @param offset from current position
  684. * @return an int[2] array where counts[0] is the number of glyphs available, and counts[1] is the number of glyphs ignored
  685. * @throws IndexOutOfBoundsException if offset or count results in an
  686. * invalid index into input glyph sequence
  687. */
  688. public int[] getGlyphsAvailable(int offset) throws IndexOutOfBoundsException {
  689. return getGlyphsAvailable(offset, offset < 0);
  690. }
  691. /**
  692. * Obtain <code>count</code> character associations of glyphs starting at specified offset from current position. If
  693. * <code>reverseOrder</code> is true, then associations are returned in reverse order starting at specified offset
  694. * and going in reverse towards beginning of input glyph sequence.
  695. * @param offset from current position
  696. * @param count number of associations to obtain
  697. * @param reverseOrder true if to obtain in reverse order
  698. * @param ignoreTester glyph tester to use to determine which glyphs are ignored (or null, in which case none are ignored)
  699. * @param associations array to use to fetch associations
  700. * @param counts int[2] array to receive fetched association counts, where counts[0] will
  701. * receive the number of associations obtained, and counts[1] will receive the number of glyphs whose
  702. * associations were ignored
  703. * @return array of associations
  704. * @throws IndexOutOfBoundsException if offset or count results in an
  705. * invalid index into input glyph sequence
  706. */
  707. public CharAssociation[] getAssociations(int offset, int count, boolean reverseOrder, GlyphTester ignoreTester, CharAssociation[] associations, int[] counts)
  708. throws IndexOutOfBoundsException {
  709. if (count < 0) {
  710. count = getGlyphsAvailable(offset, reverseOrder, ignoreTester) [ 0 ];
  711. }
  712. int start = index + offset;
  713. if (start < 0) {
  714. throw new IndexOutOfBoundsException("will attempt index at " + start);
  715. } else if (!reverseOrder && ((start + count) > indexLast)) {
  716. throw new IndexOutOfBoundsException("will attempt index at " + (start + count));
  717. } else if (reverseOrder && ((start + 1) < count)) {
  718. throw new IndexOutOfBoundsException("will attempt index at " + (start - count));
  719. }
  720. if (associations == null) {
  721. associations = new CharAssociation [ count ];
  722. } else if (associations.length != count) {
  723. throw new IllegalArgumentException("associations array is non-null, but its length (" + associations.length + "), is not equal to count (" + count + ")");
  724. }
  725. if (!reverseOrder) {
  726. return getAssociationsForward(start, count, ignoreTester, associations, counts);
  727. } else {
  728. return getAssociationsReverse(start, count, ignoreTester, associations, counts);
  729. }
  730. }
  731. private CharAssociation[] getAssociationsForward(int start, int count, GlyphTester ignoreTester, CharAssociation[] associations, int[] counts)
  732. throws IndexOutOfBoundsException {
  733. int counted = 0;
  734. int ignored = 0;
  735. for (int i = start, n = indexLast, k = 0; i < n; i++) {
  736. int gi = getGlyph(i - index);
  737. if (gi == 65535) {
  738. ignored++;
  739. } else {
  740. if ((ignoreTester == null) || !ignoreTester.test(gi, getLookupFlags())) {
  741. if (k < count) {
  742. associations [ k++ ] = getAssociation(i - index);
  743. counted++;
  744. } else {
  745. break;
  746. }
  747. } else {
  748. ignored++;
  749. }
  750. }
  751. }
  752. if ((counts != null) && (counts.length > 1)) {
  753. counts[0] = counted;
  754. counts[1] = ignored;
  755. }
  756. return associations;
  757. }
  758. private CharAssociation[] getAssociationsReverse(int start, int count, GlyphTester ignoreTester, CharAssociation[] associations, int[] counts)
  759. throws IndexOutOfBoundsException {
  760. int counted = 0;
  761. int ignored = 0;
  762. for (int i = start, k = 0; i >= 0; i--) {
  763. int gi = getGlyph(i - index);
  764. if (gi == 65535) {
  765. ignored++;
  766. } else {
  767. if ((ignoreTester == null) || !ignoreTester.test(gi, getLookupFlags())) {
  768. if (k < count) {
  769. associations [ k++ ] = getAssociation(i - index);
  770. counted++;
  771. } else {
  772. break;
  773. }
  774. } else {
  775. ignored++;
  776. }
  777. }
  778. }
  779. if ((counts != null) && (counts.length > 1)) {
  780. counts[0] = counted;
  781. counts[1] = ignored;
  782. }
  783. return associations;
  784. }
  785. /**
  786. * Obtain <code>count</code> character associations of glyphs starting at specified offset from current position. If
  787. * offset is negative, then search backwards in input glyph sequence. Uses the
  788. * default ignores tester.
  789. * @param offset from current position
  790. * @param count number of associations to obtain
  791. * @return array of associations
  792. * @throws IndexOutOfBoundsException if offset or count results in an
  793. * invalid index into input glyph sequence
  794. */
  795. public CharAssociation[] getAssociations(int offset, int count) throws IndexOutOfBoundsException {
  796. return getAssociations(offset, count, offset < 0, ignoreDefault, null, null);
  797. }
  798. /**
  799. * Obtain <code>count</code> character associations of ignored glyphs starting at specified offset from current position. If
  800. * <code>reverseOrder</code> is true, then glyphs are returned in reverse order starting at specified offset
  801. * and going in reverse towards beginning of input glyph sequence.
  802. * @param offset from current position
  803. * @param count number of character associations to obtain
  804. * @param reverseOrder true if to obtain in reverse order
  805. * @param ignoreTester glyph tester to use to determine which glyphs are ignored (or null, in which case none are ignored)
  806. * @param associations array to use to fetch associations
  807. * @param counts int[2] array to receive fetched association counts, where counts[0] will
  808. * receive the number of associations obtained, and counts[1] will receive the number of glyphs whose
  809. * associations were ignored
  810. * @return array of associations
  811. * @throws IndexOutOfBoundsException if offset or count results in an
  812. * invalid index into input glyph sequence
  813. */
  814. public CharAssociation[] getIgnoredAssociations(int offset, int count, boolean reverseOrder, GlyphTester ignoreTester, CharAssociation[] associations, int[] counts)
  815. throws IndexOutOfBoundsException {
  816. return getAssociations(offset, count, reverseOrder, new NotGlyphTester(ignoreTester), associations, counts);
  817. }
  818. /**
  819. * Obtain <code>count</code> character associations of ignored glyphs starting at specified offset from current position. If
  820. * offset is negative, then search backwards in input glyph sequence. Uses the
  821. * default ignores tester.
  822. * @param offset from current position
  823. * @param count number of character associations to obtain
  824. * @return array of associations
  825. * @throws IndexOutOfBoundsException if offset or count results in an
  826. * invalid index into input glyph sequence
  827. */
  828. public CharAssociation[] getIgnoredAssociations(int offset, int count) throws IndexOutOfBoundsException {
  829. return getIgnoredAssociations(offset, count, offset < 0, ignoreDefault, null, null);
  830. }
  831. /**
  832. * Replace subsequence of input glyph sequence starting at specified offset from current position and of
  833. * length <code>count</code> glyphs with a subsequence of the sequence <code>gs</code> starting from the specified
  834. * offset <code>gsOffset</code> of length <code>gsCount</code> glyphs.
  835. * @param offset from current position
  836. * @param count number of glyphs to replace, which, if negative means all glyphs from offset to end of input sequence
  837. * @param gs glyph sequence from which to obtain replacement glyphs
  838. * @param gsOffset offset of first glyph in replacement sequence
  839. * @param gsCount count of glyphs in replacement sequence starting at <code>gsOffset</code>
  840. * @return true if replacement occurred, or false if replacement would result in no change to input glyph sequence
  841. * @throws IndexOutOfBoundsException if offset or count results in an
  842. * invalid index into input glyph sequence
  843. */
  844. public boolean replaceInput(int offset, int count, GlyphSequence gs, int gsOffset, int gsCount) throws IndexOutOfBoundsException {
  845. int nig = (igs != null) ? igs.getGlyphCount() : 0;
  846. int position = getPosition() + offset;
  847. if (position < 0) {
  848. position = 0;
  849. } else if (position > nig) {
  850. position = nig;
  851. }
  852. if ((count < 0) || ((position + count) > nig)) {
  853. count = nig - position;
  854. }
  855. int nrg = (gs != null) ? gs.getGlyphCount() : 0;
  856. if (gsOffset < 0) {
  857. gsOffset = 0;
  858. } else if (gsOffset > nrg) {
  859. gsOffset = nrg;
  860. }
  861. if ((gsCount < 0) || ((gsOffset + gsCount) > nrg)) {
  862. gsCount = nrg - gsOffset;
  863. }
  864. int ng = nig + gsCount - count;
  865. IntBuffer gb = IntBuffer.allocate(ng);
  866. List al = new ArrayList(ng);
  867. for (int i = 0, n = position; i < n; i++) {
  868. gb.put(igs.getGlyph(i));
  869. al.add(igs.getAssociation(i));
  870. }
  871. for (int i = gsOffset, n = gsOffset + gsCount; i < n; i++) {
  872. gb.put(gs.getGlyph(i));
  873. al.add(gs.getAssociation(i));
  874. }
  875. for (int i = position + count, n = nig; i < n; i++) {
  876. gb.put(igs.getGlyph(i));
  877. al.add(igs.getAssociation(i));
  878. }
  879. Buffer gbBase = gb;
  880. gbBase.flip();
  881. assert igs != null;
  882. if (igs.compareGlyphs(gb) != 0) {
  883. this.igs = new GlyphSequence(igs.getCharacters(), gb, al);
  884. this.indexLast = gb.limit();
  885. return true;
  886. } else {
  887. return false;
  888. }
  889. }
  890. /**
  891. * Replace subsequence of input glyph sequence starting at specified offset from current position and of
  892. * length <code>count</code> glyphs with all glyphs in the replacement sequence <code>gs</code>.
  893. * @param offset from current position
  894. * @param count number of glyphs to replace, which, if negative means all glyphs from offset to end of input sequence
  895. * @param gs glyph sequence from which to obtain replacement glyphs
  896. * @return true if replacement occurred, or false if replacement would result in no change to input glyph sequence
  897. * @throws IndexOutOfBoundsException if offset or count results in an
  898. * invalid index into input glyph sequence
  899. */
  900. public boolean replaceInput(int offset, int count, GlyphSequence gs) throws IndexOutOfBoundsException {
  901. return replaceInput(offset, count, gs, 0, gs.getGlyphCount());
  902. }
  903. /**
  904. * Erase glyphs in input glyph sequence starting at specified offset from current position, where each glyph
  905. * in the specified <code>glyphs</code> array is matched, one at a time, and when a (forward searching) match is found
  906. * in the input glyph sequence, the matching glyph is replaced with the glyph index 65535.
  907. * @param offset from current position
  908. * @param glyphs array of glyphs to erase
  909. * @return the number of glyphs erased, which may be less than the number of specified glyphs
  910. * @throws IndexOutOfBoundsException if offset or count results in an
  911. * invalid index into input glyph sequence
  912. */
  913. public int erase(int offset, int[] glyphs) throws IndexOutOfBoundsException {
  914. int start = index + offset;
  915. if ((start < 0) || (start > indexLast)) {
  916. throw new IndexOutOfBoundsException("will attempt index at " + start);
  917. } else {
  918. int erased = 0;
  919. for (int i = start - index, n = indexLast - start; i < n; i++) {
  920. int gi = getGlyph(i);
  921. if (gi == glyphs [ erased ]) {
  922. setGlyph(i, 65535);
  923. erased++;
  924. }
  925. }
  926. return erased;
  927. }
  928. }
  929. /**
  930. * Determine if is possible that the current input sequence satisfies a script specific
  931. * context testing predicate. If no predicate applies, then application is always possible.
  932. * @return true if no script specific context tester applies or if a specified tester returns
  933. * true for the current input sequence context
  934. */
  935. public boolean maybeApplicable() {
  936. if (gct == null) {
  937. return true;
  938. } else {
  939. return gct.test(script, language, feature, igs, index, getLookupFlags());
  940. }
  941. }
  942. /**
  943. * Apply default application semantices; namely, consume one glyph.
  944. */
  945. public void applyDefault() {
  946. consumed += 1;
  947. }
  948. /**
  949. * Determine if specified glyph is a base glyph according to the governing
  950. * glyph definition table.
  951. * @param gi glyph index to test
  952. * @return true if glyph definition table records glyph as a base glyph; otherwise, false
  953. */
  954. public boolean isBase(int gi) {
  955. if (gdef != null) {
  956. return gdef.isGlyphClass(gi, GlyphDefinitionTable.GLYPH_CLASS_BASE);
  957. } else {
  958. return false;
  959. }
  960. }
  961. /**
  962. * Determine if specified glyph is an ignored base glyph according to the governing
  963. * glyph definition table.
  964. * @param gi glyph index to test
  965. * @param flags that apply to lookup in scope
  966. * @return true if glyph definition table records glyph as a base glyph; otherwise, false
  967. */
  968. public boolean isIgnoredBase(int gi, int flags) {
  969. return ((flags & GlyphSubtable.LF_IGNORE_BASE) != 0) && isBase(gi);
  970. }
  971. /**
  972. * Determine if specified glyph is an ligature glyph according to the governing
  973. * glyph definition table.
  974. * @param gi glyph index to test
  975. * @return true if glyph definition table records glyph as a ligature glyph; otherwise, false
  976. */
  977. public boolean isLigature(int gi) {
  978. if (gdef != null) {
  979. return gdef.isGlyphClass(gi, GlyphDefinitionTable.GLYPH_CLASS_LIGATURE);
  980. } else {
  981. return false;
  982. }
  983. }
  984. /**
  985. * Determine if specified glyph is an ignored ligature glyph according to the governing
  986. * glyph definition table.
  987. * @param gi glyph index to test
  988. * @param flags that apply to lookup in scope
  989. * @return true if glyph definition table records glyph as a ligature glyph; otherwise, false
  990. */
  991. public boolean isIgnoredLigature(int gi, int flags) {
  992. return ((flags & GlyphSubtable.LF_IGNORE_LIGATURE) != 0) && isLigature(gi);
  993. }
  994. /**
  995. * Determine if specified glyph is a mark glyph according to the governing
  996. * glyph definition table.
  997. * @param gi glyph index to test
  998. * @return true if glyph definition table records glyph as a mark glyph; otherwise, false
  999. */
  1000. public boolean isMark(int gi) {
  1001. if (gdef != null) {
  1002. return gdef.isGlyphClass(gi, GlyphDefinitionTable.GLYPH_CLASS_MARK);
  1003. } else {
  1004. return false;
  1005. }
  1006. }
  1007. /**
  1008. * Determine if specified glyph is an ignored ligature glyph according to the governing
  1009. * glyph definition table.
  1010. * @param gi glyph index to test
  1011. * @param flags that apply to lookup in scope
  1012. * @return true if glyph definition table records glyph as a ligature glyph; otherwise, false
  1013. */
  1014. public boolean isIgnoredMark(int gi, int flags) {
  1015. if ((flags & GlyphSubtable.LF_IGNORE_MARK) != 0) {
  1016. return isMark(gi);
  1017. } else if ((flags & GlyphSubtable.LF_MARK_ATTACHMENT_TYPE) != 0) {
  1018. int lac = (flags & GlyphSubtable.LF_MARK_ATTACHMENT_TYPE) >> 8;
  1019. int gac = gdef.getMarkAttachClass(gi);
  1020. return (gac != lac);
  1021. } else {
  1022. return false;
  1023. }
  1024. }
  1025. /**
  1026. * Obtain an ignored glyph tester that corresponds to the specified lookup flags.
  1027. * @param flags lookup flags
  1028. * @return a glyph tester
  1029. */
  1030. public GlyphTester getIgnoreTester(int flags) {
  1031. if ((flags & GlyphSubtable.LF_IGNORE_BASE) != 0) {
  1032. if ((flags & (GlyphSubtable.LF_IGNORE_LIGATURE | GlyphSubtable.LF_IGNORE_MARK)) == 0) {
  1033. return ignoreBase;
  1034. } else {
  1035. return getCombinedIgnoreTester(flags);
  1036. }
  1037. }
  1038. if ((flags & GlyphSubtable.LF_IGNORE_LIGATURE) != 0) {
  1039. if ((flags & (GlyphSubtable.LF_IGNORE_BASE | GlyphSubtable.LF_IGNORE_MARK)) == 0) {
  1040. return ignoreLigature;
  1041. } else {
  1042. return getCombinedIgnoreTester(flags);
  1043. }
  1044. }
  1045. if ((flags & GlyphSubtable.LF_IGNORE_MARK) != 0) {
  1046. if ((flags & (GlyphSubtable.LF_IGNORE_BASE | GlyphSubtable.LF_IGNORE_LIGATURE)) == 0) {
  1047. return ignoreMark;
  1048. } else {
  1049. return getCombinedIgnoreTester(flags);
  1050. }
  1051. }
  1052. return null;
  1053. }
  1054. /**
  1055. * Obtain an ignored glyph tester that corresponds to the specified multiple (combined) lookup flags.
  1056. * @param flags lookup flags
  1057. * @return a glyph tester
  1058. */
  1059. public GlyphTester getCombinedIgnoreTester(int flags) {
  1060. GlyphTester[] gta = new GlyphTester [ 3 ];
  1061. int ngt = 0;
  1062. if ((flags & GlyphSubtable.LF_IGNORE_BASE) != 0) {
  1063. gta [ ngt++ ] = ignoreBase;
  1064. }
  1065. if ((flags & GlyphSubtable.LF_IGNORE_LIGATURE) != 0) {
  1066. gta [ ngt++ ] = ignoreLigature;
  1067. }
  1068. if ((flags & GlyphSubtable.LF_IGNORE_MARK) != 0) {
  1069. gta [ ngt++ ] = ignoreMark;
  1070. }
  1071. return getCombinedOrTester(gta, ngt);
  1072. }
  1073. /**
  1074. * Obtain an combined OR glyph tester.
  1075. * @param gta an array of glyph testers
  1076. * @param ngt number of glyph testers present in specified array
  1077. * @return a combined OR glyph tester
  1078. */
  1079. public GlyphTester getCombinedOrTester(GlyphTester[] gta, int ngt) {
  1080. if (ngt > 0) {
  1081. return new CombinedOrGlyphTester(gta, ngt);
  1082. } else {
  1083. return null;
  1084. }
  1085. }
  1086. /**
  1087. * Obtain an combined AND glyph tester.
  1088. * @param gta an array of glyph testers
  1089. * @param ngt number of glyph testers present in specified array
  1090. * @return a combined AND glyph tester
  1091. */
  1092. public GlyphTester getCombinedAndTester(GlyphTester[] gta, int ngt) {
  1093. if (ngt > 0) {
  1094. return new CombinedAndGlyphTester(gta, ngt);
  1095. } else {
  1096. return null;
  1097. }
  1098. }
  1099. /** combined OR glyph tester */
  1100. private static class CombinedOrGlyphTester implements GlyphTester {
  1101. private GlyphTester[] gta;
  1102. private int ngt;
  1103. CombinedOrGlyphTester(GlyphTester[] gta, int ngt) {
  1104. this.gta = gta;
  1105. this.ngt = ngt;
  1106. }
  1107. /** {@inheritDoc} */
  1108. public boolean test(int gi, int flags) {
  1109. for (int i = 0, n = ngt; i < n; i++) {
  1110. GlyphTester gt = gta [ i ];
  1111. if (gt != null) {
  1112. if (gt.test(gi, flags)) {
  1113. return true;
  1114. }
  1115. }
  1116. }
  1117. return false;
  1118. }
  1119. }
  1120. /** combined AND glyph tester */
  1121. private static class CombinedAndGlyphTester implements GlyphTester {
  1122. private GlyphTester[] gta;
  1123. private int ngt;
  1124. CombinedAndGlyphTester(GlyphTester[] gta, int ngt) {
  1125. this.gta = gta;
  1126. this.ngt = ngt;
  1127. }
  1128. /** {@inheritDoc} */
  1129. public boolean test(int gi, int flags) {
  1130. for (int i = 0, n = ngt; i < n; i++) {
  1131. GlyphTester gt = gta [ i ];
  1132. if (gt != null) {
  1133. if (!gt.test(gi, flags)) {
  1134. return false;
  1135. }
  1136. }
  1137. }
  1138. return true;
  1139. }
  1140. }
  1141. /** NOT glyph tester */
  1142. private static class NotGlyphTester implements GlyphTester {
  1143. private GlyphTester gt;
  1144. NotGlyphTester(GlyphTester gt) {
  1145. this.gt = gt;
  1146. }
  1147. /** {@inheritDoc} */
  1148. public boolean test(int gi, int flags) {
  1149. if (gt != null) {
  1150. if (gt.test(gi, flags)) {
  1151. return false;
  1152. }
  1153. }
  1154. return true;
  1155. }
  1156. }
  1157. }